org.wso2.carbon.inbound.amazonsqs.AmazonSQSPollingConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.inbound.amazonsqs.AmazonSQSPollingConsumer.java

Source

/**
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 * <p>
 * WSO2 Inc. licenses this file to you 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.wso2.carbon.inbound.amazonsqs;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.DeleteMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axis2.builder.Builder;
import org.apache.axis2.builder.BuilderUtil;
import org.apache.axis2.builder.SOAPBuilder;
import org.apache.axis2.transport.TransportUtils;
import org.apache.commons.io.input.AutoCloseInputStream;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseException;
import org.apache.synapse.core.SynapseEnvironment;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.mediators.base.SequenceMediator;
import org.wso2.carbon.inbound.endpoint.protocol.generic.GenericPollingConsumer;
import com.amazonaws.services.sqs.model.Message;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * AmazonSQS inbound endpoint is used to consume messages via WSO2 ESB.
 *
 * @since 1.0.0.
 */
public class AmazonSQSPollingConsumer extends GenericPollingConsumer {

    private static final Log logger = LogFactory.getLog(AmazonSQSPollingConsumer.class.getName());
    //The time limit to wait when polling queues for messages.
    private int waitTime;
    //Maximum no of messages per poll.
    private int maxNoOfMessage;
    private BasicAWSCredentials credentials;
    private AmazonSQS sqsClient;
    //To check the connection to the Amazon SQS Queue.
    private boolean isConnected;
    //URL of the Amazon SQS Queue from which you want to consume messages.
    private String destination;
    private ReceiveMessageRequest receiveMessageRequest;
    private String messageReceiptHandle;
    //list of attributes need to be receive along with the message.
    private List<String> attributeNames;
    //Content type of the message.
    private String contentType;

    public AmazonSQSPollingConsumer(Properties amazonsqsProperties, String name,
            SynapseEnvironment synapseEnvironment, long scanInterval, String injectingSeq, String onErrorSeq,
            boolean coordination, boolean sequential) {
        super(amazonsqsProperties, name, synapseEnvironment, scanInterval, injectingSeq, onErrorSeq, coordination,
                sequential);
        this.injectingSeq = injectingSeq;
        logger.info("Starting to load the AmazonSQS Inbound Endpoint " + name);
        if (logger.isDebugEnabled()) {
            logger.debug("Starting to load the AmazonSQS Properties for " + name);
        }
        this.destination = properties.getProperty(AmazonSQSConstants.DESTINATION);
        //AccessKey to interact with Amazon SQS.
        String accessKey = properties.getProperty(AmazonSQSConstants.AMAZONSQS_ACCESSKEY);
        //SecretKey to interact with Amazon SQS.
        String secretKey = properties.getProperty(AmazonSQSConstants.AMAZONSQS_SECRETKEY);
        if (StringUtils.isEmpty(destination)) {
            throw new SynapseException("URL for the AmazonSQS Queue is empty");
        }
        if (StringUtils.isEmpty(accessKey)) {
            throw new SynapseException("Accesskey is empty");
        }
        if (StringUtils.isEmpty(secretKey)) {
            throw new SynapseException("Secretkey is empty");
        }
        if (StringUtils.isNotEmpty(properties.getProperty(AmazonSQSConstants.AMAZONSQS_SQS_WAIT_TIME))) {
            this.waitTime = Integer.parseInt(properties.getProperty(AmazonSQSConstants.AMAZONSQS_SQS_WAIT_TIME));
        } else {
            this.waitTime = 0;
        }
        if (StringUtils.isNotEmpty(properties.getProperty(AmazonSQSConstants.AMAZONSQS_SQS_MAX_NO_OF_MESSAGE))) {
            this.maxNoOfMessage = Integer
                    .parseInt(properties.getProperty(AmazonSQSConstants.AMAZONSQS_SQS_MAX_NO_OF_MESSAGE));
        } else {
            this.maxNoOfMessage = 1;
        }
        if (waitTime < 0 || waitTime > 20) {
            throw new SynapseException(
                    "Value " + waitTime + " for parameter WaitTimeSeconds is invalid. Must be >= 0 and <= 20");
        }
        if (maxNoOfMessage < 1 || maxNoOfMessage > 10) {
            throw new SynapseException("Value " + maxNoOfMessage
                    + " for parameter MaxNumberOfMessages is invalid. Must be between 1 and 10");
        }
        if (properties.getProperty(AmazonSQSConstants.ATTRIBUTE_NAMES) != null) {
            this.attributeNames = Arrays
                    .asList(properties.getProperty(AmazonSQSConstants.ATTRIBUTE_NAMES).split(","));
        } else {
            //if attribute names are not define get all the attribute with message.
            this.attributeNames = Arrays.asList(AmazonSQSConstants.ALL);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded the AmazonSQS Parameters with AccessKey : " + accessKey + " , SecretKey : "
                    + secretKey + " , Wait Time : " + waitTime + " , Maximum no of Messages : " + maxNoOfMessage
                    + " , Destination : " + destination + " , AttributeNames : " + attributeNames + " for " + name);
        }
        receiveMessageRequest = new ReceiveMessageRequest(destination);
        receiveMessageRequest.withMaxNumberOfMessages(maxNoOfMessage);
        receiveMessageRequest.withWaitTimeSeconds(waitTime);
        credentials = new BasicAWSCredentials(accessKey, secretKey);
        logger.info("Initialized the AmazonSQS inbound consumer " + name);
    }

    /**
     * Create connection with broker and retrieve the messages. Then inject
     * according to the registered handler.
     */
    public Message poll() {
        if (logger.isDebugEnabled()) {
            logger.debug("Polling AmazonSQS messages for " + name);
        }
        try {
            if (!isConnected) {
                sqsClient = new AmazonSQSClient(this.credentials);
                isConnected = true;
            }
            if (sqsClient == null) {
                logger.error("AmazonSQS Inbound endpoint " + name + " unable to get a connection.");
                isConnected = false;
                return null;
            }
            List<Message> messages;
            receiveMessageRequest.setMessageAttributeNames(attributeNames);
            messages = sqsClient.receiveMessage(receiveMessageRequest).getMessages();
            if (!messages.isEmpty()) {
                for (Message message : messages) {
                    boolean commitOrRollbacked;
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                "Injecting AmazonSQS message to the sequence : " + injectingSeq + " of " + name);
                    }
                    //Get the content type of the message.
                    if (message.getMessageAttributes().containsKey(AmazonSQSConstants.CONTENT_TYPE)) {
                        contentType = message.getMessageAttributes().get(AmazonSQSConstants.CONTENT_TYPE)
                                .getStringValue();
                        if (contentType.trim().equals("") || contentType.equals("null")) {
                            contentType = AmazonSQSConstants.DEFAULT_CONTENT_TYPE;
                        }
                    } else {
                        contentType = properties.getProperty(AmazonSQSConstants.CONTENT_TYPE);
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Loading the Content-type : " + contentType + " for " + name);
                    }
                    commitOrRollbacked = injectMessage(message.getBody(), contentType);
                    if (commitOrRollbacked) {
                        messageReceiptHandle = message.getReceiptHandle();
                        sqsClient.deleteMessage(new DeleteMessageRequest(destination, messageReceiptHandle));
                    }
                }
            } else {
                return null;
            }
        } catch (AmazonServiceException e) {
            throw new SynapseException("Caught an AmazonServiceException, which means your "
                    + "request made it to Amazon SQS, but was rejected with an" + "error response for some reason.",
                    e);
        } catch (AmazonClientException e) {
            throw new SynapseException("Caught an AmazonClientException, which means the client"
                    + " encountered a serious internal problem while trying to communicate with SQS, "
                    + "such as not being able to access the network.", e);
        }
        return null;
    }

    /**
     * Inject the message into the sequence.
     *
     */
    @Override
    protected boolean injectMessage(String strMessage, String contentType) {
        AutoCloseInputStream in = new AutoCloseInputStream(new ByteArrayInputStream(strMessage.getBytes()));
        try {
            MessageContext msgCtx = this.createMessageContext();
            if (logger.isDebugEnabled()) {
                logger.debug(
                        "Processed Custom inbound EP Message of Content-type : " + contentType + " for " + name);
            }
            org.apache.axis2.context.MessageContext axis2MsgCtx = ((Axis2MessageContext) msgCtx)
                    .getAxis2MessageContext();
            Builder builder;
            if (StringUtils.isEmpty(contentType)) {
                logger.warn("Unable to determine content type for message, setting to text/plain for " + name);
                contentType = AmazonSQSConstants.DEFAULT_CONTENT_TYPE;
            }
            int index = contentType.indexOf(';');
            String type = index > 0 ? contentType.substring(0, index) : contentType;
            builder = BuilderUtil.getBuilderFromSelector(type, axis2MsgCtx);
            if (builder == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug(
                            "No message builder found for type '" + type + "'. Falling back to SOAP. for" + name);
                }
                builder = new SOAPBuilder();
            }
            OMElement documentElement = builder.processDocument(in, contentType, axis2MsgCtx);
            //Inject the message to the sequence.
            msgCtx.setEnvelope(TransportUtils.createSOAPEnvelope(documentElement));
            if (this.injectingSeq == null || "".equals(this.injectingSeq)) {
                logger.error("Sequence name not specified. Sequence : " + this.injectingSeq + " for " + name);
                return false;
            }
            SequenceMediator seq = (SequenceMediator) this.synapseEnvironment.getSynapseConfiguration()
                    .getSequence(this.injectingSeq);
            seq.setErrorHandler(this.onErrorSeq);
            if (seq != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("injecting message to sequence : " + this.injectingSeq + " of " + name);
                }
                if (!this.synapseEnvironment.injectInbound(msgCtx, seq, this.sequential)) {
                    return false;
                }
            } else {
                logger.error("Sequence: " + this.injectingSeq + " not found for " + name);
            }
            if (isRollback(msgCtx)) {
                return false;
            }
        } catch (Exception e) {
            throw new SynapseException("Error while processing the Amazon SQS Message ", e);
        }
        return true;
    }

    /**
     * Check whether the message is rollbacked or not.
     *
     */
    private boolean isRollback(org.apache.synapse.MessageContext msgCtx) {
        // First check for rollback property from synapse context.
        Object rollbackProp = msgCtx.getProperty(AmazonSQSConstants.SET_ROLLBACK_ONLY);
        if (rollbackProp != null) {
            if ((rollbackProp instanceof Boolean && ((Boolean) rollbackProp))
                    || (rollbackProp instanceof String && Boolean.valueOf((String) rollbackProp))) {
                return true;
            }
            return false;
        }
        return false;
    }

    /**
     * Create the message context.
     *
     */
    private MessageContext createMessageContext() {
        MessageContext msgCtx = this.synapseEnvironment.createMessageContext();
        org.apache.axis2.context.MessageContext axis2MsgCtx = ((Axis2MessageContext) msgCtx)
                .getAxis2MessageContext();
        axis2MsgCtx.setServerSide(true);
        axis2MsgCtx.setMessageID(UUIDGenerator.getUUID());
        return msgCtx;
    }

    /**
     * Close the connection to the Amazon SQS.
     */
    public void destroy() {
        try {
            if (sqsClient != null) {
                sqsClient.shutdown();
                if (logger.isDebugEnabled()) {
                    logger.debug("The AmazonSQS has been shutdown ! for " + name);
                }
            }
        } catch (Exception e) {
            logger.error("Error while shutdown the AmazonSQS " + name + " " + e.getMessage(), e);
        }
    }
}