io.qdb.server.input.RabbitMQInputHandler.java Source code

Java tutorial

Introduction

Here is the source code for io.qdb.server.input.RabbitMQInputHandler.java

Source

/*
 * Copyright 2013 David Tinker
 *
 * 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 io.qdb.server.input;

import com.rabbitmq.client.*;
import io.qdb.server.ExpectedIOException;
import io.qdb.server.model.Input;
import io.qdb.server.model.Queue;

import java.io.IOException;
import java.net.ConnectException;

/**
 * Fetches messages from a RabbitMQ queue.
 */
public class RabbitMQInputHandler extends InputHandlerAdapter implements ShutdownListener {

    public String exchange;
    public String queue;
    public String routingKey = "";
    public int heartbeat = 30;
    public boolean autoAck;

    protected String inputPath;
    protected ConnectionFactory connectionFactory;
    protected Connection con;
    protected Channel channel;
    protected Sink sink;

    protected String exchangeType;
    protected boolean exchangeDurable;
    protected boolean queueDurable;

    @SuppressWarnings("unchecked")
    @Override
    public void init(Queue q, Input input, String inputPath) throws Exception {
        super.init(q, input, inputPath);
        this.inputPath = inputPath;

        if (input.getUrl() == null)
            throw new IllegalArgumentException("url is required");

        if (queue != null && queue.length() == 0)
            queue = null;
        if (exchange != null && exchange.length() == 0)
            exchange = null;

        if (queue == null) {
            if (exchange == null)
                throw new IllegalArgumentException("queue is required");
        } else {
            String[] toks = queue.split("[\\s]*#[\\s]*");
            queue = toks[0];
            queueDurable = toks.length == 1 || "true".equals(toks[1]);
            if (queue.length() == 0)
                throw new IllegalArgumentException("empty queue name");
        }

        if (exchange != null) {
            String[] toks = exchange.split("[\\s]*#[\\s]*");
            exchange = toks[0];
            exchangeType = toks.length >= 2 ? toks[1] : "fanout";
            exchangeDurable = toks.length < 3 || "true".equals(toks[2]);
        }

        connectionFactory = new ConnectionFactory();
        connectionFactory.setUri(input.getUrl());
        connectionFactory.setRequestedHeartbeat(heartbeat);
        ensureChannel();
    }

    @Override
    public void start(final Sink sink) throws Exception {
        this.sink = sink;
        final Channel c = ensureChannel();
        c.basicConsume(queue, autoAck, "qdb:" + inputPath, new DefaultConsumer(c) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                boolean ok = false;
                try {
                    sink.append(envelope.getRoutingKey(), body);
                    if (!autoAck)
                        c.basicAck(envelope.getDeliveryTag(), false);
                    ok = true;
                } catch (Exception e) {
                    sink.error(e);
                } finally {
                    if (!ok) {
                        try {
                            // todo should probably sit on the message for a bit before nacking to prevent storm
                            c.basicNack(envelope.getDeliveryTag(), false, true);
                        } catch (IOException e) {
                            log.debug("Error nacking message: " + e, e);
                        }
                    }
                }
            }
        });
    }

    @Override
    public void close() throws IOException {
        // this method cannot be synchronized or we get deadlock with shutdownCompleted
        if (con != null)
            con.close();
    }

    protected synchronized Channel ensureChannel() throws Exception {
        if (channel == null) {
            try {
                con = connectionFactory.newConnection();
            } catch (ConnectException e) {
                throw new ExpectedIOException(e.getMessage() + ": " + getConnectionInfo());
            }
            channel = con.createChannel();
            if (log.isInfoEnabled())
                log.info(inputPath + ": Connected to " + getConnectionInfo());
            channel.addShutdownListener(this);
            initChannel(channel);
        }
        return channel;
    }

    protected void initChannel(Channel channel) throws IOException {
        channel.queueDeclare(queue, queueDurable, false, false, null);
        if (exchange != null) {
            channel.exchangeDeclare(exchange, exchangeType, exchangeDurable);
            channel.queueBind(queue, exchange, routingKey);
        }
    }

    @Override
    public synchronized void shutdownCompleted(ShutdownSignalException cause) {
        channel = null;
        if (!cause.isInitiatedByApplication()) {
            try {
                if (con.isOpen())
                    con.close();
            } catch (Exception ignore) {
            }
            con = null;
            sink.error("Channel closed unexpectedly: " + cause.getMessage(), null);
        }
    }

    protected String getConnectionInfo() {
        if (connectionFactory == null)
            return "(null)";
        String host = connectionFactory.getHost();
        if (host.length() == 0)
            host = "mac-subs-009";
        return "amqp" + (connectionFactory.isSSL() ? "s" : "") + "://" + connectionFactory.getUsername() + "@"
                + host + ":" + connectionFactory.getPort() + connectionFactory.getVirtualHost();
    }
}