org.graylog2.inputs.transports.AmqpTransport.java Source code

Java tutorial

Introduction

Here is the source code for org.graylog2.inputs.transports.AmqpTransport.java

Source

/**
 * This file is part of Graylog.
 *
 * Graylog is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Graylog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Graylog.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.graylog2.inputs.transports;

import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.rabbitmq.client.ConnectionFactory;
import org.graylog2.plugin.configuration.fields.BooleanField;
import org.graylog2.plugin.inputs.annotations.ConfigClass;
import org.graylog2.plugin.inputs.annotations.FactoryClass;
import org.graylog2.plugin.LocalMetricRegistry;
import org.graylog2.plugin.configuration.Configuration;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.NumberField;
import org.graylog2.plugin.configuration.fields.TextField;
import org.graylog2.plugin.inputs.MessageInput;
import org.graylog2.plugin.inputs.MisfireException;
import org.graylog2.plugin.inputs.codecs.CodecAggregator;
import org.graylog2.plugin.inputs.transports.ThrottleableTransport;
import org.graylog2.plugin.inputs.transports.Transport;
import org.graylog2.plugin.lifecycles.Lifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Named;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;

public class AmqpTransport extends ThrottleableTransport {
    public static final String CK_HOSTNAME = "broker_hostname";
    public static final String CK_PORT = "broker_port";
    public static final String CK_VHOST = "broker_vhost";
    public static final String CK_USERNAME = "broker_username";
    public static final String CK_PASSWORD = "broker_password";
    public static final String CK_PREFETCH = "prefetch";
    public static final String CK_EXCHANGE = "exchange";
    public static final String CK_QUEUE = "queue";
    public static final String CK_ROUTING_KEY = "routing_key";
    public static final String CK_PARALLEL_QUEUES = "parallel_queues";
    public static final String CK_TLS = "tls";
    public static final String CK_REQUEUE_INVALID_MESSAGES = "requeue_invalid_messages";
    public static final String CK_HEARTBEAT_TIMEOUT = "heartbeat";

    private static final Logger LOG = LoggerFactory.getLogger(AmqpTransport.class);

    private final Configuration configuration;
    private final EventBus eventBus;
    private final MetricRegistry localRegistry;
    private final ScheduledExecutorService scheduler;

    private AmqpConsumer consumer;

    @AssistedInject
    public AmqpTransport(@Assisted Configuration configuration, EventBus eventBus,
            LocalMetricRegistry localRegistry, @Named("daemonScheduler") ScheduledExecutorService scheduler) {
        super(eventBus, configuration);
        this.configuration = configuration;
        this.eventBus = eventBus;
        this.localRegistry = localRegistry;
        this.scheduler = scheduler;

        localRegistry.register("read_bytes_1sec", new Gauge<Long>() {
            @Override
            public Long getValue() {
                return consumer.getLastSecBytesRead().get();
            }
        });
        localRegistry.register("written_bytes_1sec", new Gauge<Long>() {
            @Override
            public Long getValue() {
                return 0L;
            }
        });
        localRegistry.register("read_bytes_total", new Gauge<Long>() {
            @Override
            public Long getValue() {
                return consumer.getTotalBytesRead().get();
            }
        });
        localRegistry.register("written_bytes_total", new Gauge<Long>() {
            @Override
            public Long getValue() {
                return 0L;
            }
        });
    }

    @Subscribe
    public void lifecycleChanged(Lifecycle lifecycle) {
        try {
            LOG.debug("Lifecycle changed to {}", lifecycle);
            switch (lifecycle) {
            case PAUSED:
            case FAILED:
            case HALTING:
                try {
                    if (consumer != null) {
                        consumer.stop();
                    }
                } catch (IOException e) {
                    LOG.warn("Unable to stop consumer", e);
                }
                break;
            default:
                if (consumer.isConnected()) {
                    LOG.debug("Consumer is already connected, not running it a second time.");
                    break;
                }
                try {
                    consumer.run();
                } catch (IOException e) {
                    LOG.warn("Unable to resume consumer", e);
                }
                break;
            }
        } catch (Exception e) {
            LOG.warn("This should not throw any exceptions", e);
        }
    }

    @Override
    public void setMessageAggregator(CodecAggregator aggregator) {

    }

    @Override
    public void doLaunch(MessageInput input) throws MisfireException {
        int heartbeatTimeout = ConnectionFactory.DEFAULT_HEARTBEAT;
        if (configuration.intIsSet(CK_HEARTBEAT_TIMEOUT)) {
            heartbeatTimeout = configuration.getInt(CK_HEARTBEAT_TIMEOUT);
            if (heartbeatTimeout < 0) {
                LOG.warn("AMQP heartbeat interval must not be negative ({}), using default timeout ({}).",
                        heartbeatTimeout, ConnectionFactory.DEFAULT_HEARTBEAT);
                heartbeatTimeout = ConnectionFactory.DEFAULT_HEARTBEAT;
            }
        }
        consumer = new AmqpConsumer(configuration.getString(CK_HOSTNAME), configuration.getInt(CK_PORT),
                configuration.getString(CK_VHOST), configuration.getString(CK_USERNAME),
                configuration.getString(CK_PASSWORD), configuration.getInt(CK_PREFETCH),
                configuration.getString(CK_QUEUE), configuration.getString(CK_EXCHANGE),
                configuration.getString(CK_ROUTING_KEY), configuration.getInt(CK_PARALLEL_QUEUES),
                configuration.getBoolean(CK_TLS), configuration.getBoolean(CK_REQUEUE_INVALID_MESSAGES),
                heartbeatTimeout, input, scheduler, this);
        eventBus.register(this);
        try {
            consumer.run();
        } catch (IOException e) {
            eventBus.unregister(this);
            throw new MisfireException("Could not launch AMQP consumer.", e);
        }
    }

    @Override
    public void doStop() {
        if (consumer != null) {
            try {
                consumer.stop();
            } catch (IOException e) {
                LOG.error("Could not stop AMQP consumer.", e);
            }
        }
        eventBus.unregister(this);
    }

    @Override
    public com.codahale.metrics.MetricSet getMetricSet() {
        return localRegistry;
    }

    @FactoryClass
    public interface Factory extends Transport.Factory<AmqpTransport> {
        @Override
        AmqpTransport create(Configuration configuration);

        @Override
        Config getConfig();
    }

    @ConfigClass
    public static class Config extends ThrottleableTransport.Config {
        @Override
        public ConfigurationRequest getRequestedConfiguration() {
            final ConfigurationRequest cr = super.getRequestedConfiguration();
            cr.addField(new TextField(CK_HOSTNAME, "Broker hostname", "", "Hostname of the AMQP broker to use",
                    ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new NumberField(CK_PORT, "Broker port", 5672, "Port of the AMQP broker to use",
                    ConfigurationField.Optional.OPTIONAL, NumberField.Attribute.IS_PORT_NUMBER));

            cr.addField(new TextField(CK_VHOST, "Broker virtual host", "/",
                    "Virtual host of the AMQP broker to use", ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new TextField(CK_USERNAME, "Username", "", "Username to connect to AMQP broker",
                    ConfigurationField.Optional.OPTIONAL));

            cr.addField(new TextField(CK_PASSWORD, "Password", "", "Password to connect to AMQP broker",
                    ConfigurationField.Optional.OPTIONAL, TextField.Attribute.IS_PASSWORD));

            cr.addField(new NumberField(CK_PREFETCH, "Prefetch count", 100,
                    "For advanced usage: AMQP prefetch count. Default is 100.",
                    ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new TextField(CK_QUEUE, "Queue", defaultQueueName(), "Name of queue that is created.",
                    ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new TextField(CK_EXCHANGE, "Exchange", defaultExchangeName(),
                    "Name of exchange to bind to.", ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new TextField(CK_ROUTING_KEY, "Routing key", defaultRoutingKey(),
                    "Routing key to listen for.", ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(new NumberField(CK_PARALLEL_QUEUES, "Number of Queues", 1, "Number of parallel Queues",
                    ConfigurationField.Optional.NOT_OPTIONAL));

            cr.addField(
                    new NumberField(CK_HEARTBEAT_TIMEOUT, "Heartbeat timeout", ConnectionFactory.DEFAULT_HEARTBEAT,
                            "Heartbeat interval in seconds (use 0 to disable heartbeat)",
                            ConfigurationField.Optional.OPTIONAL));

            cr.addField(new BooleanField(CK_TLS, "Enable TLS?", false,
                    "Enable transport encryption via TLS. (requires valid TLS port setting)"));

            cr.addField(new BooleanField(CK_REQUEUE_INVALID_MESSAGES, "Re-queue invalid messages?", true,
                    "Invalid messages will be discarded if disabled."));

            return cr;
        }

        protected String defaultRoutingKey() {
            return "#";
        }

        protected String defaultExchangeName() {
            return "log-messages";
        }

        protected String defaultQueueName() {
            return "log-messages";
        }
    }
}