org.flockdata.client.amqp.FdRabbitClient.java Source code

Java tutorial

Introduction

Here is the source code for org.flockdata.client.amqp.FdRabbitClient.java

Source

/*
 *  Copyright 2012-2016 the original author or authors.
 *
 *  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 org.flockdata.client.amqp;

import com.rabbitmq.client.*;
import org.flockdata.helper.JsonUtils;
import org.flockdata.integration.AmqpRabbitConfig;
import org.flockdata.integration.ClientConfiguration;
import org.flockdata.registration.TagInputBean;
import org.flockdata.track.bean.EntityInputBean;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.TimeoutException;

/**
 * Writes entity and tag payloads to FlockData over AMQP
 * <p>
 * @author mholdsworth
 * @since 27/11/2014
 * @tag FdClient, Rabbit, Messaging
 */
@Service
@Profile({ "fd-server", "fd-client" })
public class FdRabbitClient {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(FdRabbitClient.class);
    private final ClientConfiguration configuration;
    private final AmqpRabbitConfig rabbitConfig;
    private AMQP.BasicProperties entityProps;
    private AMQP.BasicProperties tagProps;
    private Connection connection;

    private ConnectionFactory connectionFactory = null;

    private Channel trackChannel = null; // Channel is cached but not thread safe

    @Autowired
    public FdRabbitClient(ClientConfiguration clientConfiguration, AmqpRabbitConfig rabbitConfig) {
        this.configuration = clientConfiguration;
        this.rabbitConfig = rabbitConfig;
    }

    @PostConstruct
    public void logStatus() {
        logger.info("** Configured for Rabbit on {}", rabbitConfig.getHost());
    }

    @PreDestroy
    public void shuttingDown() {
        logger.debug("Jumping out of the rabbit hole due to shutdown request");
    }

    private void verifyConnection() {

        try {
            initConnectionFactory();

            if ((trackChannel == null || connection == null) || !(connection.isOpen() && trackChannel.isOpen())) {
                openConnection();
            }
        } catch (TimeoutException | IOException e) {
            logger.error("Unexpected error initializing Rabbit connection. Is it running @ {}:{}? {}",
                    rabbitConfig.getHost(), rabbitConfig.getPort(), e.getMessage());
        }

    }

    private void initConnectionFactory() throws IOException, TimeoutException {
        if (connectionFactory == null) {
            logger.debug("Initializing Rabbit connection");
            entityProps = null;
            tagProps = null;

            // Had issues with injecting amqpRabbitConfig.connectionFactory into this class
            // Connection was randomly closed in integration tests

            connectionFactory = new ConnectionFactory();
            connectionFactory.setAutomaticRecoveryEnabled(true);
            connectionFactory.setHost(rabbitConfig.getHost());
            connectionFactory.setPort(rabbitConfig.getPort());
            connectionFactory.setPassword(rabbitConfig.getPass());
            connectionFactory.setUsername(rabbitConfig.getUser());
            connectionFactory.setExceptionHandler(new ExceptionHandler() {
                @Override
                public void handleUnexpectedConnectionDriverException(Connection conn, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleReturnListenerException(Channel channel, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleFlowListenerException(Channel channel, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleConfirmListenerException(Channel channel, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleBlockedListenerException(Connection connection, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleConsumerException(Channel channel, Throwable exception, Consumer consumer,
                        String consumerTag, String methodName) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleConnectionRecoveryException(Connection conn, Throwable exception) {

                }

                @Override
                public void handleChannelRecoveryException(Channel ch, Throwable exception) {
                    logger.info(exception.getMessage());
                }

                @Override
                public void handleTopologyRecoveryException(Connection conn, Channel ch,
                        TopologyRecoveryException exception) {
                    logger.info(exception.getMessage());
                }
            });
        }
    }

    private void openConnection() throws IOException, TimeoutException {
        if (connectionFactory == null)
            initConnectionFactory();

        connection = connectionFactory.newConnection();
        connection.addShutdownListener(cause -> {
            Method reason = cause.getReason();
            if (cause.isHardError()) {
                if (!cause.isInitiatedByApplication()) {
                    logger.debug("Hard shutdown initiate - {} {} {}",
                            (reason != null ? reason.protocolMethodName() : "null protocol method"),
                            cause.getReason(), cause.getMessage());
                }
            } else {
                logger.debug("Shutdown initiated - {} {} {}",
                        (reason != null ? reason.protocolMethodName() : "null protocol method"), cause.getReason(),
                        cause.getMessage());
            }
            connection = null;
            connectionFactory = null;
        });

        trackChannel = connection.createChannel();
        logger.debug("{}/{}/{}", configuration.getTrackQueue(), configuration.getFdExchange(),
                configuration.getTrackRoutingKey());
        logger.debug(trackChannel.queueBind(configuration.getTrackQueue(), configuration.getFdExchange(),
                configuration.getTrackRoutingKey(), rabbitConfig.getFdQueueFeatures()).toString());

    }

    private String getApiKey() {
        if (configuration.getApiKey() == null || configuration.getApiKey().equals(""))
            throw new RuntimeException("No API key is set. Please configure one and try again");
        return configuration.getApiKey();

    }

    private AMQP.BasicProperties getTagProps() {
        if (tagProps == null)
            tagProps = new AMQP.BasicProperties().builder().headers(getHeaders("T", getApiKey()))
                    .deliveryMode(rabbitConfig.getPersistentDelivery() ? 2 : null).replyTo("nullChannel").build();
        return tagProps;
    }

    private AMQP.BasicProperties getEntityProps() {
        if (entityProps == null)
            entityProps = new AMQP.BasicProperties().builder().headers(getHeaders("E", getApiKey()))
                    .deliveryMode(rabbitConfig.getPersistentDelivery() ? 2 : null).replyTo("nullChannel").build();
        return entityProps;
    }

    public void publish(Collection<EntityInputBean> entityInputs) throws IOException {
        verifyConnection();
        assert trackChannel.isOpen();
        trackChannel.basicPublish(configuration.getFdExchange(), configuration.getTrackRoutingKey(),
                getEntityProps(), JsonUtils.toJsonBytes(entityInputs));
    }

    public void publishTags(Collection<TagInputBean> tagInputs) throws IOException {
        verifyConnection();
        trackChannel.basicPublish(configuration.getFdExchange(), configuration.getTrackRoutingKey(), getTagProps(),
                JsonUtils.toJsonBytes(tagInputs));
    }

    private HashMap<String, Object> getHeaders(String type, String apiKey) {
        HashMap<String, Object> headers;
        headers = new HashMap<>();
        headers.put(ClientConfiguration.KEY_MSG_KEY, apiKey);
        headers.put(ClientConfiguration.KEY_MSG_TYPE, type);
        return headers;
    }

    /**
     * Used by integration tests to reset the port number.
     *
     * @param rabbitHost url
     * @param rabbitPort port
     */
    public void resetRabbitClient(String rabbitHost, Integer rabbitPort) {
        if (!(Objects.equals(rabbitHost, rabbitConfig.getHost())
                && Objects.equals(rabbitPort, rabbitConfig.getPort()))) {
            rabbitConfig.resetHost(rabbitHost, rabbitPort);
            logger.info("Resetting Rabbit client clientConnection to {}:{}", rabbitConfig.getHost(),
                    rabbitConfig.getPort());
            verifyConnection();
        }

    }

}