it.polimi.hegira.queue.ServiceQueue.java Source code

Java tutorial

Introduction

Here is the source code for it.polimi.hegira.queue.ServiceQueue.java

Source

/**
 * Copyright 2015 Marco Scavuzzo
 * Contact: Marco Scavuzzo <marco.scavuzzo@polimi.it>
 *
 * 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.
 */
package it.polimi.hegira.queue;

import it.polimi.hegira.exceptions.QueueException;
import it.polimi.hegira.utils.DefaultSerializer;

import java.io.IOException;

import org.apache.log4j.Logger;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;

/**
 * Service queue which allows the interaction between the API component and the SRC or TWC
 * @author Marco Scavuzzo
 *
 */
public class ServiceQueue {
    private static Logger log = Logger.getLogger(ServiceQueue.class);

    private ConnectionFactory factory;
    private Connection connection;
    private Channel channelPublish;
    private Channel channelConsume;
    private static final String EXCHANGE_NAME = "service-queue";
    public static final String API_PUBLISH_RK = "toApiServer";
    private String LISTEN_RK;
    private QueueingConsumer consumer;

    /**
     * Creates a service queue between the given component and hegira-api.
     * @param componentType The type of component creating the queue, i.e. SRC or TWC.
     * @param queueAddress The address of RabbitMQ broker.
     */
    public ServiceQueue(String componentType, String queueAddress) {
        LISTEN_RK = componentType;

        factory = new ConnectionFactory();
        factory.setHost(queueAddress);
        try {
            connection = factory.newConnection();
            //SENDER PART
            channelPublish = connection.createChannel();
            /**
             * direct exchange: a message goes to the queues whose binding key 
             * exactly matches the routing key of the message.
             */
            channelPublish.exchangeDeclare(EXCHANGE_NAME, "direct");
            log.debug("Publish channel created. Exchange: " + EXCHANGE_NAME + " type: direct");

            //RECEIVER PART
            channelConsume = connection.createChannel();
            channelConsume.exchangeDeclare(EXCHANGE_NAME, "direct");
            log.debug("Consuming channel created. Exchange: " + EXCHANGE_NAME + " type: direct");

            //String queueName = channelConsume.queueDeclare().getQueue();
            /**
             * queueDeclare(java.lang.String queue, boolean durable, 
             * boolean exclusive, boolean autoDelete, 
             * java.util.Map<java.lang.String,java.lang.Object> arguments) 
             */
            String queueName;
            switch (componentType) {
            case "SRC":
                queueName = channelConsume.queueDeclare("Q2", false, false, false, null).getQueue();
                break;
            case "TWC":
                queueName = channelConsume.queueDeclare("Q3", false, false, false, null).getQueue();
                break;
            default:
                queueName = channelConsume.queueDeclare().getQueue();
                break;
            }

            /**
             * routing key bindings: relationship between an exchange and a queue.
             * Binds the queue, just created, with an exchange and with a routing key.
             */
            channelConsume.queueBind(queueName, EXCHANGE_NAME, LISTEN_RK);
            log.debug("Binding the consuming channel. ROUTING KEY: " + LISTEN_RK + " QUEUE NAME: " + queueName);

            /**
             * Telling the server to deliver us the messages from the queue. 
             * Since it will push us messages asynchronously, we provide a callback 
             * in the form of an object (QueueingConsumer) that will buffer the messages 
             * until we're ready to use them.
             */
            consumer = new QueueingConsumer(channelConsume);
            /**
             * basicConsume(java.lang.String queue, boolean autoAck, Consumer callback)
             * Starts a non-nolocal, non-exclusive consumer, 
             * with a server-generated consumerTag.
             */
            channelConsume.basicConsume(queueName, true, consumer);
            log.debug("Consumer started on queue: " + queueName);
        } catch (IOException e) {
            log.error(e.toString());
        }
    }

    /**
     * Writes a message in the queue with the given routing key.
     * @param routingKey The routing key.
     * @param messageBody The message to be sent.
     * @throws QueueException if an error has occurred.
     */
    public void publish(String routingKey, byte[] messageBody) throws QueueException {
        /**
         * Declaring a queue is idempotent - it will only be created if it doesn't exist already. 
         * basicPublish(java.lang.String exchange, 
         *       java.lang.String routingKey, 
         *       AMQP.BasicProperties props, byte[] body)
         */
        try {
            //declare Q1 regardless if it already exists or not ...
            String queueName = channelConsume.queueDeclare("Q1", false, false, false, null).getQueue();
            //... and bind it to the proper Exchange and routingKey
            channelPublish.queueBind(queueName, EXCHANGE_NAME, routingKey);

            channelPublish.basicPublish(EXCHANGE_NAME, routingKey, null, messageBody);
            log.debug("Message published. ROUTING KEY: " + routingKey);
        } catch (IOException e) {
            throw new QueueException();
        }
    }

    /**
     * Used by the SRC or the TWC to announce their presence to hegira-api component.
     * @throws QueueException
     */
    public void announcePresence() throws QueueException {
        publish(API_PUBLISH_RK, LISTEN_RK.getBytes());
    }

    /**
     * Listens for commands sent by hegira-api component
     * @return
     * @throws QueueException
     */
    public ServiceQueueMessage receiveCommands() throws QueueException {

        /**
         * QueueingConsumer.nextDelivery() blocks 
         * until another message has been delivered from the server.
         */
        QueueingConsumer.Delivery delivery;
        try {
            log.debug("waiting for messages");
            delivery = consumer.nextDelivery();
            byte[] message = delivery.getBody();
            //String routingKey = delivery.getEnvelope().getRoutingKey();

            try {
                ServiceQueueMessage sqm = (ServiceQueueMessage) DefaultSerializer.deserialize(message);
                return sqm;

            } catch (ClassNotFoundException | IOException e) {
                // TODO Auto-generated catch block

                e.printStackTrace();
            }

        } catch (ShutdownSignalException | ConsumerCancelledException | InterruptedException e) {
            throw new QueueException();
        }

        return null;

    }

    /**
     * Gets queuing consumer.
     * @return The queuing consumer.
     */
    public QueueingConsumer getConsumer() {
        return consumer;
    }

}