All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jashmore.sqs.util.LocalSqsAsyncClientImpl Maven / Gradle / Ivy

package com.jashmore.sqs.util;

import static com.jashmore.sqs.util.SqsQueuesConfig.DEFAULT_SQS_SERVER_URL;
import static com.jashmore.sqs.util.SqsQueuesConfig.QueueConfig.DEFAULT_MAX_RECEIVE_COUNT;
import static java.util.stream.Collectors.toList;
import static software.amazon.awssdk.services.sqs.model.QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES;
import static software.amazon.awssdk.services.sqs.model.QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE;

import com.jashmore.sqs.util.concurrent.CompletableFutureUtils;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsAsyncClient;
import software.amazon.awssdk.services.sqs.SqsAsyncClientBuilder;
import software.amazon.awssdk.services.sqs.model.CreateQueueResponse;
import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse;
import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse;
import software.amazon.awssdk.services.sqs.model.ListQueuesResponse;
import software.amazon.awssdk.services.sqs.model.PurgeQueueResponse;
import software.amazon.awssdk.services.sqs.model.QueueAttributeName;
import software.amazon.awssdk.services.sqs.model.SendMessageRequest;
import software.amazon.awssdk.services.sqs.model.SendMessageResponse;
import software.amazon.awssdk.utils.SdkAutoCloseable;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;

/**
 * Helper implementation of the {@link SqsAsyncClient} that can be used to connect to a locally running SQS server.
 *
 * 

The implementation is kept to an absolute minimum and it will only connect to the server and set up any queues that should be set up on initial * connection. For any further configuration options look at creating your own implementation. */ @SuppressWarnings("unused") @Slf4j public class LocalSqsAsyncClientImpl implements LocalSqsAsyncClient { @Delegate(excludes = SdkAutoCloseable.class) private final SqsAsyncClient delegate; public LocalSqsAsyncClientImpl(final SqsQueuesConfig sqsQueuesConfig) { this(sqsQueuesConfig, (builder) -> { }); } public LocalSqsAsyncClientImpl(final SqsQueuesConfig sqsQueuesConfig, final Consumer clientBuilderConsumer) { final String serverUrl = Optional.ofNullable(sqsQueuesConfig.getSqsServerUrl()) .orElse(DEFAULT_SQS_SERVER_URL); log.info("Connecting to local SQS service at {}", serverUrl); final URI serverUri; try { serverUri = new URI(serverUrl); } catch (URISyntaxException uriSyntaxException) { throw new RuntimeException("Invalid Server URL for SQS Server", uriSyntaxException); } final SqsAsyncClientBuilder clientBuilder = SqsAsyncClient.builder() .endpointOverride(serverUri) .region(Region.of("localstack")) .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("accessKeyId", "secretAccessKey"))); clientBuilderConsumer.accept(clientBuilder); delegate = clientBuilder.build(); if (!sqsQueuesConfig.getQueues().isEmpty()) { try { buildQueues(delegate, sqsQueuesConfig).get(); } catch (InterruptedException interruptedException) { Thread.currentThread().interrupt(); throw new RuntimeException("Interrupted waiting for queues to be built", interruptedException); } catch (ExecutionException executionException) { throw new RuntimeException("Error building initial queues", executionException.getCause()); } } } public LocalSqsAsyncClientImpl(final SqsAsyncClient delegate) { this.delegate = delegate; } @Override public CompletableFuture sendMessage(final String queueName, final String messageBody) { return sendMessage(queueName, builder -> builder.messageBody(messageBody)); } @Override public CompletableFuture sendMessage(final String queueName, final SendMessageRequest sendMessageRequest) { return getQueueUrl(builder -> builder.queueName(queueName)) .thenApply(GetQueueUrlResponse::queueUrl) .thenCompose(queueUrl -> sendMessage(sendMessageRequest.toBuilder() .queueUrl(queueUrl) .build())); } @Override public CompletableFuture sendMessage(final String queueName, final Consumer sendMessageRequestBuilderConsumer) { return getQueueUrl(builder -> builder.queueName(queueName)) .thenApply(GetQueueUrlResponse::queueUrl) .thenCompose(queueUrl -> sendMessage(builder -> { sendMessageRequestBuilderConsumer.accept(builder); builder.queueUrl(queueUrl); })); } @Override public CompletableFuture createRandomQueue() { final String queueName = UUID.randomUUID().toString().replace("-", ""); log.info("Creating queue with name: {}", queueName); return createQueue((requestBuilder) -> requestBuilder.queueName(queueName).build()) .thenApply(createQueueResponse -> CreateRandomQueueResponse.builder() .response(createQueueResponse) .queueName(queueName) .build()); } @Override public CompletableFuture> purgeAllQueues() { return delegate.listQueues() .thenApply(ListQueuesResponse::queueUrls) .thenCompose(queueUrls -> CompletableFutureUtils.allOf(queueUrls.stream() .map(url -> purgeQueue(builder -> builder.queueUrl(url))) .collect(toList()) )); } @Override public CompletableFuture getApproximateMessages(final String queueName) { return getQueueUrl(builder -> builder.queueName(queueName)) .thenApply(GetQueueUrlResponse::queueUrl) .thenCompose(queueUrl -> getQueueAttributes(builder -> builder.queueUrl(queueUrl) .attributeNames(APPROXIMATE_NUMBER_OF_MESSAGES, APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE))) .thenApply(GetQueueAttributesResponse::attributes) .thenApply(attributes -> Integer.parseInt(attributes.get(APPROXIMATE_NUMBER_OF_MESSAGES)) + Integer.parseInt(attributes.get(APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE))); } /** * Build all of the queues that are required for this local client once the bean has been created. */ private static CompletableFuture> buildQueues(SqsAsyncClient delegate, SqsQueuesConfig sqsQueuesConfig) { final List> queueFutures = sqsQueuesConfig.getQueues().stream() .map(queueConfig -> buildQueue(delegate, sqsQueuesConfig, queueConfig)) .collect(toList()); return CompletableFutureUtils.allOf(queueFutures); } private static CompletableFuture buildQueue(SqsAsyncClient delegate, SqsQueuesConfig sqsQueuesConfig, SqsQueuesConfig.QueueConfig queueConfig) { log.debug("Creating local queue: {}", queueConfig.getQueueName()); final Map attributes = new HashMap<>(); if (queueConfig.getVisibilityTimeout() != null) { attributes.put(QueueAttributeName.VISIBILITY_TIMEOUT, String.valueOf(queueConfig.getVisibilityTimeout())); } final CompletableFuture createDeadLetterQueueFuture; if (queueConfig.getMaxReceiveCount() != null || queueConfig.getDeadLetterQueueName() != null) { final String deadLetterQueueName = Optional.ofNullable(queueConfig.getDeadLetterQueueName()) .orElse(queueConfig.getQueueName() + "-dlq"); final int maxReceiveCount = Optional.ofNullable(queueConfig.getMaxReceiveCount()) .orElse(DEFAULT_MAX_RECEIVE_COUNT); createDeadLetterQueueFuture = createDeadLetterQueue(delegate, sqsQueuesConfig, deadLetterQueueName) .thenAccept(deadLetterQueueArn -> attributes.put( QueueAttributeName.REDRIVE_POLICY, String.format("{\"deadLetterTargetArn\":\"%s\",\"maxReceiveCount\":\"%d\"}", deadLetterQueueArn, maxReceiveCount))); } else { createDeadLetterQueueFuture = CompletableFuture.completedFuture(null); } return createDeadLetterQueueFuture .thenCompose(ignored -> delegate.createQueue(builder -> builder .queueName(queueConfig.getQueueName()) .attributes(attributes)) ); } /** * Create a Dead Letter Queue that should be used for the queue with the provided name. * * @param queueName the name of the queue that this dead letter queue is for * @return the future with the queue ARN of the dead letter queue created */ private static CompletableFuture createDeadLetterQueue(SqsAsyncClient delegate, SqsQueuesConfig sqsQueuesConfig, final String queueName) { log.debug("Creating dead letter queue: {}", queueName); return delegate.createQueue((builder -> builder.queueName(queueName))) .thenCompose(createQueueResponse -> delegate.getQueueAttributes(builder -> builder .queueUrl(createQueueResponse.queueUrl()) .attributeNames(QueueAttributeName.QUEUE_ARN)) ) .thenApply(queueAttributes -> queueAttributes.attributes().get(QueueAttributeName.QUEUE_ARN)); } @Override public void close() { log.info("Closing local SDK"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy