coyote.dx.reader.RabbitReader.java Source code

Java tutorial

Introduction

Here is the source code for coyote.dx.reader.RabbitReader.java

Source

/*
 * Copyright (c) 2017 Stephan D. Cote' - All rights reserved.
 * 
 * This program and the accompanying materials are made available under the 
 * terms of the MIT License which accompanies this distribution, and is 
 * available at http://creativecommons.org/licenses/MIT/
 *
 * Contributors:
 *   Stephan D. Cote 
 *      - Initial concept and implementation
 */
package coyote.dx.reader;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

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

import coyote.commons.CipherUtil;
import coyote.commons.ExceptionUtil;
import coyote.commons.StringUtil;
import coyote.dataframe.DataFrame;
import coyote.dataframe.marshal.JSONMarshaler;
import coyote.dataframe.marshal.MarshalException;
import coyote.dataframe.marshal.XMLMarshaler;
import coyote.dx.ConfigTag;
import coyote.dx.ConfigurableComponent;
import coyote.dx.FrameReader;
import coyote.dx.context.TransactionContext;
import coyote.dx.context.TransformContext;
import coyote.loader.Loader;
import coyote.loader.log.Log;
import coyote.loader.log.LogMsg;
import coyote.mq.CMQ;

/**
 * 
 */
public class RabbitReader extends AbstractFrameReader implements FrameReader, ConfigurableComponent {

    private static final boolean NO_AUTO_ACK = false;
    private static final boolean REQUEUE = true;
    private static final boolean DURABLE = true;
    private static final boolean PUBLIC = false;
    private static final boolean KEEP = false;
    private static final Map<String, Object> NO_ARGUMENTS = null;

    private Connection connection = null;
    private Channel channel = null;
    private int prefetchCount = 1;
    private boolean peekEofCheck = true;

    public URI getBrokerURI() {
        if (configuration.containsIgnoreCase(ConfigTag.SOURCE)) {
            URI retval;
            try {
                retval = new URI(configuration.getString(ConfigTag.SOURCE));
                return retval;
            } catch (URISyntaxException e) {
                Log.debug(LogMsg.createMsg(CMQ.MSG, "Reader.config_attribute_is_not_valid_uri", ConfigTag.SOURCE,
                        configuration.getString(ConfigTag.SOURCE)));
            }
        }
        return null;
    }

    public String getPassword() {
        if (configuration.containsIgnoreCase(ConfigTag.PASSWORD)) {
            return configuration.getString(ConfigTag.PASSWORD);
        } else if (configuration.containsIgnoreCase(Loader.ENCRYPT_PREFIX + ConfigTag.PASSWORD)) {
            return CipherUtil.decryptString(configuration.getString(Loader.ENCRYPT_PREFIX + ConfigTag.PASSWORD));
        } else {
            return null;
        }
    }

    public String getUsername() {
        if (configuration.containsIgnoreCase(ConfigTag.USERNAME)) {
            return configuration.getString(ConfigTag.USERNAME);
        } else if (configuration.containsIgnoreCase(Loader.ENCRYPT_PREFIX + ConfigTag.USERNAME)) {
            return CipherUtil.decryptString(configuration.getString(Loader.ENCRYPT_PREFIX + ConfigTag.USERNAME));
        } else {
            return null;
        }
    }

    public boolean useSSL() {
        if (configuration.containsIgnoreCase(ConfigTag.USE_SSL)) {
            return configuration.getBoolean(ConfigTag.USE_SSL);
        }
        return false;
    }

    public boolean isListening() {
        if (configuration.containsIgnoreCase(ConfigTag.LISTEN)) {
            return configuration.getBoolean(ConfigTag.LISTEN);
        }
        return false;
    }

    public String getQueueName() {
        if (configuration.containsIgnoreCase(ConfigTag.QUEUE)) {
            return configuration.getString(ConfigTag.QUEUE);
        }
        return null;
    }

    /**
     * @see coyote.dx.reader.AbstractFrameReader#open(coyote.dx.context.TransformContext)
     */
    @Override
    public void open(TransformContext context) {
        super.open(context);

        ConnectionFactory factory = new ConnectionFactory();

        try {
            factory.setUri(getBrokerURI());
            if (useSSL()) {
                factory.useSslProtocol();
            }

            String username = getUsername();
            if (StringUtil.isNotBlank(username)) {
                factory.setUsername(username);
                factory.setPassword(getPassword());
            }

            connection = factory.newConnection();
            channel = connection.createChannel();
            channel.basicQos(prefetchCount);
            channel.queueDeclare(getQueueName(), DURABLE, PUBLIC, KEEP, NO_ARGUMENTS);
        } catch (KeyManagementException | NoSuchAlgorithmException | URISyntaxException | IOException
                | TimeoutException | ShutdownSignalException | ConsumerCancelledException e) {
            Log.error(e.getClass().getSimpleName() + ":" + e.getMessage() + "\n" + ExceptionUtil.stackTrace(e));
            getContext().setError("Could not open " + getClass().getSimpleName() + ": " + e.getMessage());
        }

    }

    /**
     * @see coyote.dx.FrameReader#read(coyote.dx.context.TransactionContext)
     */
    @Override
    public DataFrame read(TransactionContext context) {
        DataFrame retval = null;
        try {
            while (retval == null) {
                GetResponse response = channel.basicGet(getQueueName(), NO_AUTO_ACK);
                if (response != null) {
                    byte[] data = null;
                    try {
                        data = response.getBody();
                        channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
                    } catch (IOException e) {
                        Log.error("Could not get data from message body: " + e.getClass().getName() + " - "
                                + e.getMessage());
                    }
                    if (data != null) {
                        try {
                            retval = new DataFrame(data);
                        } catch (Exception e) {
                            Log.debug("Received data not in dataframe wire format");
                            String text = StringUtil.getString(data);
                            try {
                                List<DataFrame> frames = JSONMarshaler.marshal(text);
                                if (frames != null && frames.size() > 0) {
                                    retval = frames.get(0);
                                } else {
                                    Log.notice("Received an empty JSON message");
                                }
                            } catch (MarshalException e1) {
                                Log.debug("Received data not in JSON format");
                                try {
                                    List<DataFrame> frames = XMLMarshaler.marshal(text);
                                    if (frames != null && frames.size() > 0) {
                                        retval = frames.get(0);
                                    } else {
                                        Log.notice("Received an empty XML message");
                                    }
                                } catch (MarshalException e2) {
                                    Log.error("Could not parse the data received from " + channel.toString());
                                }
                            }
                        }
                    } else {
                        Log.warn("Retrieved an empty body from a message: "
                                + response.getEnvelope().getDeliveryTag());
                    }
                } else {
                    // If we are not in listen mode, break out of the loop and return null, otherwise loop
                    if (!isListening()) {
                        break;
                    }
                }
            }
        } catch (IOException e) {
            Log.warn("Exception on message retrieval: " + e.getClass().getName() + " - " + e.getMessage());
        }
        return retval;
    }

    /**
     * @see coyote.dx.FrameReader#eof()
     */
    @Override
    public boolean eof() {
        boolean retval = true;
        if (isListening()) {
            retval = false;
        } else {
            if (peekEofCheck) {
                try {
                    GetResponse response = channel.basicGet(getQueueName(), NO_AUTO_ACK);
                    if (response != null) {
                        retval = false;
                        try {
                            channel.basicReject(response.getEnvelope().getDeliveryTag(), REQUEUE);
                        } catch (Exception e) {
                            Log.error("Could not requeue message on EOF check: " + e.getClass().getName() + " - "
                                    + e.getMessage());
                        }
                    }
                } catch (IOException e) {
                    Log.error("Exception on EOF check: " + e.getClass().getName() + " - " + e.getMessage());
                }
            }
        }
        return retval;
    }

    /**
     * @see coyote.dx.reader.AbstractFrameReader#close()
     */
    @Override
    public void close() throws IOException {
        // perform our closing functions first
        if (connection != null) {
            connection.close();
        }

        // perform base class closing functions last
        super.close();
    }

}