Package com.softwaremill.common.sqs

Source Code of com.softwaremill.common.sqs.Queue

package com.softwaremill.common.sqs;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.*;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.softwaremill.common.sqs.exception.SQSRuntimeException;

import java.io.IOException;
import java.io.Serializable;

import static com.google.common.base.Preconditions.*;
import static java.lang.String.format;
import static com.softwaremill.common.sqs.Util.deserializeFromBase64;

/**
* @author Maciej Bilas
* @since 11/10/12 12:43
*/
public class Queue {
    private static final Logger LOG = LoggerFactory.getLogger(Queue.class);

    private static final int DELIVERY_RETRY_LIMIT = 10;
    private static final int DELIVERY_RETRY_SLEEP_TIME_IN_MILLIS = 1000;

    private final String name;
    private final String url;
    private final AmazonSQS sqsClient;

    @SuppressWarnings("UnusedDeclaration")
    public Queue() {
        LOG.error("Don't use this constructor. It's here only to make CDI happy.");
        this.name = null;
        this.url = null;
        this.sqsClient = null;
    }

    public Queue(String name, String url, AmazonSQS sqsClient) {
        this.name = name;
        this.url = checkNotNull(url);
        this.sqsClient = checkNotNull(sqsClient);
    }

    public String getURL() {
        return url;
    }

    private MessageId sendSerializableInternal(Serializable object, Optional<Duration> duration) {
        String encodedMessage;
        try {
            encodedMessage = Util.serializeToBase64(object);
        } catch (IOException e) {
            throw new SQSRuntimeException("Could not serialize message.", e);
        }

        checkState(encodedMessage.length() <= 64 * 1024);

        LOG.debug("Serialized Message: " + encodedMessage);

        SendMessageRequest request = new SendMessageRequest(url, encodedMessage);
        if (duration.isPresent()) {
            request.withDelaySeconds((int) duration.get().getStandardSeconds());
        }

        for (int i = 0; i < DELIVERY_RETRY_LIMIT; ++i) {
            try {
                /* No verification of message checksum is done at this point. It might get added in the future, though. */

                return new MessageId(sqsClient.sendMessage(request).getMessageId());
            } catch (AmazonServiceException e) {
                LOG.warn(format("Could not sent message to SQS queue: %s. Retrying.", url), e);
                try {
                    Thread.sleep(DELIVERY_RETRY_SLEEP_TIME_IN_MILLIS);
                } catch (InterruptedException e1) {
                    // Ignore
                }
            }
        }
        throw new SQSRuntimeException("Exceeded redelivery value: " + DELIVERY_RETRY_LIMIT + "; message not sent!");
    }

    public MessageId sendSerializable(Serializable object) {
        checkNotNull(object);

        return sendSerializableInternal(object, Optional.<Duration>absent());
    }

    public MessageId sendSerializableDelayed(Serializable object, Duration duration) {
        checkNotNull(object);
        checkNotNull(duration);
        checkArgument(duration.getStandardSeconds() < 15 * 60,
                "SQS messages can only be delayed for a maximum of 15 minutes.");

        long droppedMillis = duration.getMillis() % 1000;
        if (droppedMillis != 0)
            LOG.warn(format("SQS delayed messages have a precision of 1s, milliseconds will be stripped. " +
                    "Object to send: %s Millis: %s", object.toString(), duration.getMillis()));

        if (duration.getMillis() < 1000) {
            return sendSerializable(object);
        } else {
            return sendSerializableInternal(object, Optional.of(duration));
        }
    }

    public Optional<ReceivedMessage> receiveSingleMessage() {
        LOG.debug(format("Polling queue %s", url));

        ReceiveMessageResult response = sqsClient.receiveMessage(new ReceiveMessageRequest(url)
                .withMaxNumberOfMessages(1).withAttributeNames("SentTimestamp"));

        switch (response.getMessages().size()) {
            case 0:
                return Optional.absent();
            case 1:
                return Optional.fromNullable(decodeMessage(response.getMessages().get(0)));
            default:
                /* This seems very unlikely.
                 * The API specified the following: SQS never returns more messages than this value but might return fewer.
                 * http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/APIReference/Query_QueryReceiveMessage.html
                 */
                throw new IllegalStateException();
        }

    }

    private ReceivedMessage decodeMessage(Message message) {
        String receiptHandle = message.getReceiptHandle();
        try {
            /* Again no MD5 verification, yet */
            return new ReceivedMessage(deserializeFromBase64(message.getBody()),
                    new ReceiptHandle(receiptHandle),
                    new MessageId(message.getMessageId()));
        } catch (IOException e) {
            LOG.warn(format("Could not deserialize message from the queue %s.", name));
            LOG.debug(format("Message body of unrecognized message %s", message.getBody()));
            delete(receiptHandle);
            return null;
        } catch (ClassNotFoundException e) {
            LOG.warn(format("Could not deserialize message from the queue %s.", name));
            LOG.debug(format("Message body of unrecognized message %s", message.getBody()));
            delete(receiptHandle);
            return null;
        }
    }

    private void delete(String receiptHandle) {
        sqsClient.deleteMessage(new DeleteMessageRequest(url, receiptHandle));
    }

    public void deleteMessage(ReceivedMessage message) {
        checkNotNull(message);

        delete(message.getReceiptHandle().get());
    }

    /**
     * @param timeout timeout in seconds for the whole queue (default is 30) - value is limited to 43200 seconds (12 hours)
     */
    public void setQueueVisibilityTimeout(int timeout) {
        checkArgument(timeout >= 0);
        checkArgument(timeout <= 12 * 60 * 60);

        sqsClient.setQueueAttributes(new SetQueueAttributesRequest(url,
                ImmutableMap.of("VisibilityTimeout", Integer.toString(timeout))));
    }


    public void setMessageVisibilityTimeout(ReceivedMessage message, int timeout) {
        checkNotNull(message);
        checkArgument(timeout >= 0);
        checkArgument(timeout <= 12 * 60 * 60);

        sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest(url,
                message.getReceiptHandle().get(), timeout));
    }

    /**
     * @param waitTimeInSeconds wait time for message in seconds, allowed values 0-20. Values > 0 allows longer message pooling
     */
    public void setReceiveMessageWaitTime(int waitTimeInSeconds) {
        checkArgument(waitTimeInSeconds >= 0);
        checkArgument(waitTimeInSeconds <= 20);

        sqsClient.setQueueAttributes(new SetQueueAttributesRequest(url,
                ImmutableMap.of("ReceiveMessageWaitTimeSeconds", Integer.toString(waitTimeInSeconds))));
    }

    @SuppressWarnings("UnusedDeclaration")
    public String getName() {
        return name;
    }
}
TOP

Related Classes of com.softwaremill.common.sqs.Queue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.