com.eucalyptus.portal.SimpleQueueClientManager.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.portal.SimpleQueueClientManager.java

Source

/*************************************************************************
 * (c) Copyright 2017 Hewlett Packard Enterprise Development Company LP
 * <p>
 * This program 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; version 3 of the License.
 * <p>
 * This program 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.
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 ************************************************************************/

package com.eucalyptus.portal;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequest;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry;
import com.amazonaws.services.sqs.model.DeleteQueueRequest;
import com.amazonaws.services.sqs.model.GetQueueUrlRequest;
import com.amazonaws.services.sqs.model.GetQueueUrlResult;
import com.amazonaws.services.sqs.model.ListQueuesRequest;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.amazonaws.services.sqs.model.SetQueueAttributesRequest;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.tokens.SecurityTokenAWSCredentialsProvider;
import com.eucalyptus.component.ServiceUris;
import com.eucalyptus.component.Topology;
import com.eucalyptus.simplequeue.SimpleQueue;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.StringReader;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class SimpleQueueClientManager {
    private static final Logger LOG = Logger.getLogger(SimpleQueueClientManager.class);

    private static final ObjectMapper mapper = new ObjectMapper()
            .setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE);
    static {
        // to prevent conflicting setter definitions for property "seed"
        mapper.addMixIn(ClientConfiguration.class, ClientConfigurationMixin.class);
    }

    private static SimpleQueueClientManager instance = new SimpleQueueClientManager();

    private SimpleQueueClientManager() {
        simpleQueueClient = buildClient();
    }

    public static SimpleQueueClientManager getInstance() {
        return instance;
    }

    private AmazonSQS simpleQueueClient = null;

    public AmazonSQS getSimpleQueueClient() {
        if (simpleQueueClient == null) {
            simpleQueueClient = buildClient();
        }
        return simpleQueueClient;
    }

    private AmazonSQS buildClient() {
        try {
            return buildClient(BillingAWSCredentialsProvider.BillingUserSupplier.INSTANCE,
                    BillingProperties.SQS_CLIENT_CONFIG);
        } catch (final Exception ex) {
            LOG.error("Failed to initialize SQS client", ex);
            return null;
        }
    }

    private AmazonSQS buildClient(final Supplier<User> user, final String text) throws AuthException {
        final AWSCredentialsProvider credentialsProvider = new SecurityTokenAWSCredentialsProvider(user);
        final AmazonSQS client = new AmazonSQSClient(credentialsProvider, buildConfiguration(text));
        client.setEndpoint(ServiceUris.remote(Topology.lookup(SimpleQueue.class)).toString());
        return client;
    }

    @SuppressWarnings("unused")
    private interface ClientConfigurationMixin {
        @JsonIgnore
        SecureRandom getSecureRandom();

        @JsonIgnore
        void setSecureRandom(SecureRandom secureRandom);
    }

    /**
     * Parse a JSON format string for AWS SDK for Java ClientConfiguration.
     *
     * @param text The configuration in JSON
     * @return The configuration object
     */
    public static ClientConfiguration buildConfiguration(final String text) {
        try {
            return Strings.isNullOrEmpty(text) ? new ClientConfiguration()
                    : mapper.readValue(source(text), ClientConfiguration.class);
        } catch (final IOException e) {
            throw new IllegalArgumentException("Invalid configuration: " + e.getMessage(), e);
        }
    }

    private static StringReader source(final String text) {
        return new StringReader(text) {
            @Override
            public String toString() {
                return "property";
            } // overridden for better source in error message
        };
    }

    final LoadingCache<String, String> queueUrlCache = CacheBuilder.newBuilder().maximumSize(10000)
            .expireAfterWrite(10, TimeUnit.MINUTES).build(new CacheLoader<String, String>() {
                @Override
                public String load(final String queueName) throws Exception {
                    try {
                        final GetQueueUrlRequest req = new GetQueueUrlRequest();
                        req.setQueueName(queueName);
                        req.setQueueOwnerAWSAccountId(BillingAWSCredentialsProvider.BillingUserSupplier.INSTANCE
                                .get().getAccountNumber());
                        final GetQueueUrlResult result = getSimpleQueueClient().getQueueUrl(req);
                        return result.getQueueUrl();
                    } catch (final Exception ex) {
                        throw ex;
                    }
                }
            });

    public String getQueueUrl(String queueName) {
        try {
            if (queueName.contains("/")) { // in case queue url is given
                queueName = queueName.substring(queueName.lastIndexOf('/') + 1);
            }
            return queueUrlCache.get(queueName);
        } catch (final Exception ex) {
            LOG.error("Failed to get queue url", ex);
            return null;
        }
    }

    public boolean queueExists(final String queueName) {
        return getQueueUrl(queueName) != null;
    }

    public void createQueue(final String queueName, final Map<String, String> queueAttributes) throws Exception {
        try {
            final CreateQueueRequest req = new CreateQueueRequest();
            if (queueAttributes != null)
                req.setAttributes(queueAttributes);
            req.setQueueName(queueName);
            if (getSimpleQueueClient().createQueue(req).getQueueUrl() == null)
                throw new Exception("Null queue URL is returned");
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to create queue due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to create queue due to client error", ex);
        }
    }

    public List<String> listQueues(final String prefix) throws Exception {
        try {
            final ListQueuesRequest req = new ListQueuesRequest();
            if (prefix != null)
                req.setQueueNamePrefix(prefix);
            return getSimpleQueueClient().listQueues(req).getQueueUrls();
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to list queues due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to list queues due to client error", ex);
        }
    }

    public void deleteQueue(final String queueName) throws Exception {
        try {
            final DeleteQueueRequest req = new DeleteQueueRequest();
            req.setQueueUrl(getQueueUrl(queueName));
            getSimpleQueueClient().deleteQueue(req);
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to delete queue due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to delete queue due to client error", ex);
        }
    }

    public void setQueueAttributes(final String queueName, final Map<String, String> queueAttributes)
            throws Exception {
        try {
            final SetQueueAttributesRequest req = new SetQueueAttributesRequest();
            req.setAttributes(queueAttributes);
            req.setQueueUrl(getQueueUrl(queueName));
            getSimpleQueueClient().setQueueAttributes(req);
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to set queue attributes due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to set queue attributes due to client error", ex);
        }
    }

    public void sendMessage(final String queueName, final String message) throws Exception {
        try {
            final SendMessageRequest req = new SendMessageRequest();
            req.setQueueUrl(getQueueUrl(queueName));
            req.setDelaySeconds(0);
            req.setMessageBody(message);
            getSimpleQueueClient().sendMessage(req);
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to send message due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to send message due to client error", ex);
        }
    }

    public List<Message> receiveAllMessages(final String queueName, final boolean shouldDelete) throws Exception {
        try {
            final int visibilityTimeout = 600;
            final int visibilityBuffer = 300;
            final long startTime = System.currentTimeMillis();
            final List<Message> messages = Lists.newArrayList();
            while ((System.currentTimeMillis() - startTime) < ((visibilityTimeout - visibilityBuffer) * 1000L)) {
                final ReceiveMessageRequest req = new ReceiveMessageRequest();
                req.setQueueUrl(getQueueUrl(queueName));
                req.setMaxNumberOfMessages(10);
                req.setWaitTimeSeconds(0);
                req.setVisibilityTimeout(visibilityTimeout);

                final ReceiveMessageResult result = getSimpleQueueClient().receiveMessage(req);
                final List<Message> received = result.getMessages();
                if (received == null || received.size() <= 0)
                    break;
                messages.addAll(received);
            }

            // TODO: Use PurgeQueue
            if (shouldDelete) {
                for (final List<Message> partition : Iterables.partition(messages, 10)) {
                    final DeleteMessageBatchRequest delReq = new DeleteMessageBatchRequest();
                    delReq.setQueueUrl(getQueueUrl(queueName));
                    delReq.setEntries(partition.stream().map(m -> new DeleteMessageBatchRequestEntry()
                            .withId(m.getMessageId()).withReceiptHandle(m.getReceiptHandle()))
                            .collect(Collectors.toList()));
                    getSimpleQueueClient().deleteMessageBatch(delReq);
                }
            }
            return messages;
        } catch (final AmazonServiceException ex) {
            throw new Exception("Failed to receive messages due to service error", ex);
        } catch (final AmazonClientException ex) {
            throw new Exception("Failed to receive messages due to client error", ex);
        }
    }
}