From a44d99551e02d1e281f9ba1af0e313cf99ac0bc8 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 00:37:29 +0200 Subject: [PATCH 01/14] Start with AWS SQS implementation. Sending is in theory implemented. A lot of things are missing though: logging, finalize tracing, error handling, retry, config. After that polling could be implemented. I am afraid that the tracing cannot be implemented properly due to reactive, but we will see. Tests are missing completely. --- pom.xml | 1 + smallrye-reactive-messaging-aws/pom.xml | 95 ++++++++++++++ .../smallrye-reactive-messaging-sqs/pom.xml | 29 +++++ .../reactive/messaging/aws/sqs/Clients.java | 5 + .../messaging/aws/sqs/SqsConnector.java | 119 ++++++++++++++++++ .../messaging/aws/sqs/SqsOutgoingChannel.java | 111 ++++++++++++++++ .../reactive/messaging/aws/sqs/Target.java | 37 ++++++ .../aws/sqs/action/GetQueueUrlAction.java | 25 ++++ .../sqs/action/SendBatchMessageAction.java | 92 ++++++++++++++ .../aws/sqs/action/SendMessageAction.java | 69 ++++++++++ .../messaging/aws/sqs/cache/TargetCache.java | 28 +++++ .../aws/sqs/client/SqsClientFactory.java | 16 +++ .../aws/sqs/client/SqsClientHolder.java | 36 ++++++ .../aws/sqs/config/ConfigResolver.java | 4 + .../messaging/aws/sqs/i18n/SqsExceptions.java | 27 ++++ .../messaging/aws/sqs/i18n/SqsLogging.java | 18 +++ .../messaging/aws/sqs/message/SqsMessage.java | 27 ++++ .../aws/sqs/message/SqsMessageMetadata.java | 47 +++++++ .../aws/sqs/message/SqsOutgoingMessage.java | 43 +++++++ .../message/SqsOutgoingMessageMetadata.java | 5 + .../sqs/tracing/SqsAttributesExtractor.java | 24 ++++ .../aws/sqs/tracing/SqsInstrumenter.java | 28 +++++ .../tracing/SqsMessagingAttributesGetter.java | 54 ++++++++ .../messaging/aws/sqs/tracing/SqsTrace.java | 44 +++++++ .../sqs/tracing/SqsTraceTextMapSetter.java | 14 +++ .../messaging/aws/sqs/util/Helper.java | 16 +++ .../messaging/tracing/TracingUtils.java | 2 + 27 files changed, 1016 insertions(+) create mode 100644 smallrye-reactive-messaging-aws/pom.xml create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java diff --git a/pom.xml b/pom.xml index 1356f9504f..ad3cb72d26 100644 --- a/pom.xml +++ b/pom.xml @@ -139,6 +139,7 @@ smallrye-reactive-messaging-rabbitmq smallrye-reactive-messaging-gcp-pubsub smallrye-reactive-messaging-pulsar + smallrye-reactive-messaging-aws examples/quickstart examples/kafka-quickstart examples/kafka-quickstart-kotlin diff --git a/smallrye-reactive-messaging-aws/pom.xml b/smallrye-reactive-messaging-aws/pom.xml new file mode 100644 index 0000000000..22e69b760f --- /dev/null +++ b/smallrye-reactive-messaging-aws/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + + io.smallrye.reactive + smallrye-reactive-messaging + 4.11.0-SNAPSHOT + + + smallrye-reactive-messaging-aws + + SmallRye Reactive Messaging : Connector :: AWS + + pom + + + 2.20.157 + + + + smallrye-reactive-messaging-sqs + + + + + + software.amazon.awssdk + bom + ${awssdk.version} + pom + import + + + + + + + ${project.groupId} + smallrye-reactive-messaging-provider + ${project.version} + + + + io.smallrye.config + smallrye-config + test + + + io.smallrye.reactive + smallrye-connector-attribute-processor + ${project.version} + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${project.build.directory}/generated-sources/ + + + io.smallrye.reactive.messaging.connector.ConnectorAttributeProcessor + + + org.jboss.logging.processor.apt.LoggingToolsProcessor + + + + + + + + + + coverage + + @{jacocoArgLine} + + + + + org.jacoco + jacoco-maven-plugin + + + + + + diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml new file mode 100644 index 0000000000..3cce937233 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + io.smallrye.reactive + smallrye-reactive-messaging-aws + 4.11.0-SNAPSHOT + + + smallrye-reactive-messaging-sqs + + SmallRye Reactive Messaging : Connector :: AWS SQS + + + + software.amazon.awssdk + sqs + + + io.smallrye.reactive + smallrye-reactive-messaging-otel + ${project.version} + + + + + diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java new file mode 100644 index 0000000000..f06f438835 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java @@ -0,0 +1,5 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +public class Clients { + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java new file mode 100644 index 0000000000..a2bad4c407 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -0,0 +1,119 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; +import io.smallrye.reactive.messaging.aws.sqs.cache.TargetCache; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.connector.InboundConnector; +import io.smallrye.reactive.messaging.connector.OutboundConnector; +import io.smallrye.reactive.messaging.health.HealthReporter; +import io.smallrye.reactive.messaging.json.JsonMapping; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.BeforeDestroyed; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.event.Reception; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.reactive.messaging.Message; +import org.eclipse.microprofile.reactive.messaging.spi.Connector; +import software.amazon.awssdk.services.sqs.SqsAsyncClient; +import software.amazon.awssdk.services.sqs.model.SqsException; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Flow; + +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; +import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; + +@ApplicationScoped +@Connector(SqsConnector.CONNECTOR_NAME) +// common +@ConnectorAttribute(name = "queue", type = "string", direction = INCOMING_AND_OUTGOING, description = "Set the SQS queue. If not set, the channel name is used") +@ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") + +// outgoing +@ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") + +// incomming +public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { + + static final String CONNECTOR_NAME = "smallrye-aws-sqs"; + + private final Map clients = new ConcurrentHashMap<>(); + private final Map clientsByChannel = new ConcurrentHashMap<>(); + private final List outgoingChannels = new CopyOnWriteArrayList<>(); + // private final List> incomingChannels = new CopyOnWriteArrayList<>(); + + @Inject + Instance jsonMapper; + private JsonMapping jsonMapping; + + @PostConstruct + public void init() { + if (jsonMapper.isUnsatisfied()) { + log.warn( + "Please add one of the additional mapping modules (-jsonb or -jackson) to be able to (de)serialize JSON messages."); + } else if (jsonMapper.isAmbiguous()) { + log.warn( + "Please select only one of the additional mapping modules (-jsonb or -jackson) to be able to (de)serialize JSON messages."); + this.jsonMapping = jsonMapper.stream().findFirst() + .orElseThrow(() -> new RuntimeException("Unable to find JSON Mapper")); + } else { + this.jsonMapping = jsonMapper.get(); + } + } + + @Override + public Flow.Publisher> getPublisher(Config config) { + SqsConnectorIncomingConfiguration ic = new SqsConnectorIncomingConfiguration(config); + + SqsAsyncClient client = clients.computeIfAbsent("", ignored -> createSqsClient(ic)); + clientsByChannel.put(ic.getChannel(), client); + + return null; + } + + @Override + public Flow.Subscriber> getSubscriber(Config config) { + SqsConnectorOutgoingConfiguration oc = new SqsConnectorOutgoingConfiguration(config); + + SqsAsyncClient client = clients.computeIfAbsent("", ignored -> createSqsClient(oc)); + clientsByChannel.put(oc.getChannel(), client); + + try { + SqsOutgoingChannel channel = new SqsOutgoingChannel(new SqsClientHolder<>(client, oc, jsonMapping, new TargetCache())); + outgoingChannels.add(channel); + return channel.getSubscriber(); + } catch (SqsException e) { + throw ex.illegalStateUnableToBuildConsumer(e); + } + } + + public void terminate( + @Observes(notifyObserver = Reception.IF_EXISTS) + @Priority(50) + @BeforeDestroyed(ApplicationScoped.class) Object event) { + // incomingChannels.forEach(PulsarIncomingChannel::close); + outgoingChannels.forEach(SqsOutgoingChannel::close); + for (SqsAsyncClient client : clients.values()) { + try { + client.close(); + } catch (SqsException e) { + log.unableToCloseClient(e); + } + } + // incomingChannels.clear(); + outgoingChannels.clear(); + clients.clear(); + clientsByChannel.clear(); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java new file mode 100644 index 0000000000..cca9ae3487 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -0,0 +1,111 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.tuples.Tuple2; +import io.smallrye.reactive.messaging.aws.sqs.action.SendBatchMessageAction; +import io.smallrye.reactive.messaging.aws.sqs.action.SendMessageAction; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; +import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; +import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; +import io.smallrye.reactive.messaging.tracing.TracingUtils; +import org.eclipse.microprofile.reactive.messaging.Message; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; + +public class SqsOutgoingChannel { + + private final SqsClientHolder clientHolder; + private final Flow.Subscriber> subscriber; + + public SqsOutgoingChannel(SqsClientHolder clientHolder) { + this.clientHolder = clientHolder; + + if (clientHolder.getConfig().getSendBatchEnabled()) { + subscriber = MultiUtils.via(this::createStream); + } else { + subscriber = MultiUtils.via(this::createStreamWithBatching); + } + } + + private Multi> prepare(Multi> m) { + return m + .onItem().transform(SqsOutgoingMessage::from) + .onItem().transformToUniAndConcatenate(this::addTargetInformation) + .onItem().invoke(this::tracing); + } + + private Multi createStream(Multi> m) { + return prepare(m) + .onItem().transformToUniAndConcatenate(this::sendMessage); + // .onFailure().invoke(log::unableToSend) + } + + private Multi createStreamWithBatching(Multi> m) { + // We need to group by target first and then into lists for batching. + // Reason is that queue can be overwritten in messages. We cannot group into lists directly. Otherwise, + // we would send messages to the wrong queue. + return prepare(m) + .group().by(SqsMessage::getTarget) + .onItem().transformToMultiAndMerge(group -> group + .group().intoLists().of(10, Duration.ofMillis(0)) + .onItem().transform(msg -> Tuple2.of(group.key(), msg))) + .onItem().transformToUniAndConcatenate(tuple -> sendBatchMessage(tuple.getItem1(), tuple.getItem2())); + } + + private Uni> addTargetInformation(SqsOutgoingMessage msg) { + return clientHolder.getTargetCache().getTarget(clientHolder, msg) + .onItem().transform(target -> { + msg.withTarget(target); + return msg; + }); + } + + private Uni sendMessage(SqsOutgoingMessage message) { + final SqsOutgoingMessage sqsMessage = SqsOutgoingMessage.from(message); + return clientHolder.getTargetCache().getTarget(clientHolder, sqsMessage) + .onItem().transformToUni(target -> SendMessageAction.sendMessage(clientHolder, sqsMessage)); + } + + private Uni sendBatchMessage(Target target, List> messages) { + return SendBatchMessageAction.sendMessage(clientHolder, target, messages); + } + + private void tracing(SqsOutgoingMessage message) { + if (clientHolder.getConfig().getTracingEnabled()) { + SqsTrace trace = new SqsTrace() + .withQueue(message.getTarget().getTargetName()) + // TODO: Cannot set messageId. It is provided in response. + // The intstrumenter documentation says: + // Call shouldStart(Context, Object) and do not proceed if it returns false. + // Call start(Context, Object) at the beginning of a request. + // Call end(Context, Object, Object, Throwable) at the end of a request. + // TracingUtils violates this, because end is called immediately and not at the end of a request. + // So it would be necessary to update the trace at response and then end the span. + // This is not possible. + // For batching it starts here and might be delayed extremely. This will not be visible. + // Furthermore, it is an issue to set the messageIds. They are available in the action, but + // we cannot wait until this happened. Updating messageId in response would be ok as well, but not + // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. + // .withMessageId("") + .withConversationId(message.getSqsMetadata().getConversationId()) + // We do not set payload size. This would require to calculate it, which is less performant. + ; + TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); + } + } + + public Flow.Subscriber> getSubscriber() { + return subscriber; + } + + public void close() { + + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java new file mode 100644 index 0000000000..756214d4d9 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java @@ -0,0 +1,37 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import java.util.Objects; + +public class Target { + + private final String targetName; + private final String targetUrl; + + public Target(String targetName, String targetUrl) { + this.targetName = targetName; + this.targetUrl = targetUrl; + } + + public String getTargetName() { + return targetName; + } + + public String getTargetUrl() { + return targetUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Target target = (Target) o; + return Objects.equals(targetUrl, target.targetUrl); + } + + @Override + public int hashCode() { + return Objects.hash(targetUrl); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java new file mode 100644 index 0000000000..10cc6b37fd --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java @@ -0,0 +1,25 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; +import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; + +public class GetQueueUrlAction { + + public static Uni resolveQueueUrl( + final SqsClientHolder clientHolder, final SqsMessage message) { + final SqsConnectorCommonConfiguration config = clientHolder.getConfig(); + final SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); + + return Uni.createFrom().completionStage( + clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() + .queueName(config.getQueue().orElse(config.getChannel())) + .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) + .build())) + .onItem().transform(GetQueueUrlResponse::queueUrl); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java new file mode 100644 index 0000000000..c2fbd16e08 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java @@ -0,0 +1,92 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.OutgoingMessageMetadata; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; +import io.smallrye.reactive.messaging.aws.sqs.util.Helper; +import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; +import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; +import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; + +public class SendBatchMessageAction { + + public static Uni sendMessage( + final SqsClientHolder clientHolder, + Target target, final List> messages) { + final Map entryMap = new HashMap<>(); + final Map> messageMap = new HashMap<>(); + + messages.forEach(msg -> { + final String payload = Helper.serialize(msg, clientHolder.getJsonMapping()); + final String id = UUID.randomUUID().toString(); + + final SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() + // in batching we need to generate the id of a message for every entry. + .id(id) + .messageAttributes(null) + .messageGroupId(null) + .messageBody(payload) + + .messageDeduplicationId(null) + .delaySeconds(0) + .messageSystemAttributesWithStrings(null) + .build(); + messageMap.put(id, msg); + entryMap.put(id, entry); + }); + + final SendMessageBatchRequest request = SendMessageBatchRequest.builder() + .queueUrl(target.getTargetUrl()) + .entries(entryMap.values()) + .build(); + + // TODO: logging + Uni uni = Uni.createFrom().completionStage(clientHolder.getClient().sendMessageBatch(request)) + .onItem().transformToUni(response -> handleResponse(response, messageMap)); + + // TODO: configurable retry. Retry complete batch in case the complete HTTP request fails + // but what to do in case just specific messages fails? In theory most efficient would be to retry + // them by adding them back to the stream. This seems very difficult. Another option is to do the configured + // retries for the failed once immediately until all are successful or max retries reached. + if (true) { + uni = uni.onFailure().retry() + .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) + .atMost(3); + } + + // TODO: micrometer? Failure and Success? + + return uni; + } + + private static Uni handleResponse( + SendMessageBatchResponse response, Map> messageMap) { + final Uni successful = Multi.createFrom().iterable(response.successful()) + .onItem().call(result -> { + OutgoingMessageMetadata.setResultOnMessage(messageMap.get(result.id()), result); + return Uni.createFrom().completionStage(messageMap.get(result.id()).ack()); + }).collect().last().replaceWithVoid(); + + final Uni failed = Multi.createFrom().iterable(response.failed()) + .onItem().call(result -> { + OutgoingMessageMetadata.setResultOnMessage(messageMap.get(result.id()), result); + return Uni.createFrom().completionStage(messageMap.get(result.id()) + .nack(ex.illegalStateUnableToBuildClient( + new IllegalStateException(result.code() + ": " + result.message())))); + }).collect().last().replaceWithVoid(); + + return Uni.combine().all().unis(successful, failed).discardItems(); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java new file mode 100644 index 0000000000..af5fe264a5 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -0,0 +1,69 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.OutgoingMessageMetadata; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; +import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; +import io.smallrye.reactive.messaging.aws.sqs.util.Helper; +import io.smallrye.reactive.messaging.tracing.TracingUtils; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; + +import java.time.Duration; + +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; + +public class SendMessageAction { + + public static Uni sendMessage( + final SqsClientHolder clientHolder, final SqsOutgoingMessage message) { + String payload = Helper.serialize(message, clientHolder.getJsonMapping()); + + if (clientHolder.getConfig().getTracingEnabled()) { + SqsTrace trace = new SqsTrace() + .withQueue(message.getTarget().getTargetName()) + // TODO: Cannot set messageId. It is provided in response. + // The intstrumenter documentation says: + // Call shouldStart(Context, Object) and do not proceed if it returns false. + // Call start(Context, Object) at the beginning of a request. + // Call end(Context, Object, Object, Throwable) at the end of a request. + // TracingUtils violates this, because end is called immediately and not at the end of a request. + // .withMessageId("") + .withConversationId(message.getSqsMetadata().getConversationId()) + // We do not set payload size. This would require to calculate it, which is less performant. + ; + TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); + } + + final SendMessageRequest request = SendMessageRequest.builder() + .queueUrl(message.getTarget().getTargetUrl()) + .messageAttributes(null) + .messageGroupId(null) + .messageBody(payload) + + .messageDeduplicationId(null) + .delaySeconds(0) + .messageSystemAttributesWithStrings(null) + .build(); + + // TODO: logging + Uni uni = Uni.createFrom().completionStage(clientHolder.getClient().sendMessage(request)) + .onItem().transformToUni(response -> { + OutgoingMessageMetadata.setResultOnMessage(message, response); + // TODO: log + return Uni.createFrom().completionStage(message.ack()); + }); + + // TODO: configurable retry + if (true) { + uni = uni.onFailure().retry() + .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) + .atMost(3); + } + + // TODO: micrometer? Failure and Success? + + return uni; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java new file mode 100644 index 0000000000..50ad3ae685 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java @@ -0,0 +1,28 @@ +package io.smallrye.reactive.messaging.aws.sqs.cache; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.aws.sqs.action.GetQueueUrlAction; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class TargetCache { + + private final Map> CACHE = new ConcurrentHashMap<>(); + + public Uni getTarget( + final SqsClientHolder clientHolder, final SqsMessage message) { + final SqsConnectorCommonConfiguration config = clientHolder.getConfig(); + + return CACHE.computeIfAbsent( + clientHolder.getConfig().getQueue().orElse(config.getChannel()), + key -> GetQueueUrlAction.resolveQueueUrl(clientHolder, message) + .onItem().transform(url -> new Target(key, url)) + .memoize().indefinitely()); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java new file mode 100644 index 0000000000..46c79e3260 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java @@ -0,0 +1,16 @@ +package io.smallrye.reactive.messaging.aws.sqs.client; + +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; +import software.amazon.awssdk.services.sqs.SqsAsyncClient; + +public class SqsClientFactory { + + public static SqsAsyncClient createSqsClient(final SqsConnectorIncomingConfiguration config) { + return null; + } + + public static SqsAsyncClient createSqsClient(final SqsConnectorOutgoingConfiguration config) { + return null; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java new file mode 100644 index 0000000000..ddf8fd8b80 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -0,0 +1,36 @@ +package io.smallrye.reactive.messaging.aws.sqs.client; + +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.cache.TargetCache; +import io.smallrye.reactive.messaging.json.JsonMapping; +import software.amazon.awssdk.services.sqs.SqsAsyncClient; + +public class SqsClientHolder { + private final SqsAsyncClient client; + private final C config; + private final JsonMapping jsonMapping; + private final TargetCache targetCache; + + public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, TargetCache targetCache) { + this.client = client; + this.config = config; + this.jsonMapping = jsonMapping; + this.targetCache = targetCache; + } + + public SqsAsyncClient getClient() { + return client; + } + + public C getConfig() { + return config; + } + + public JsonMapping getJsonMapping() { + return jsonMapping; + } + + public TargetCache getTargetCache() { + return targetCache; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java new file mode 100644 index 0000000000..a14ee078e6 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java @@ -0,0 +1,4 @@ +package io.smallrye.reactive.messaging.aws.sqs.config; + +public class ConfigResolver { +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java new file mode 100644 index 0000000000..a239879296 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java @@ -0,0 +1,27 @@ +package io.smallrye.reactive.messaging.aws.sqs.i18n; + +import org.jboss.logging.Messages; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageBundle; + +@MessageBundle(projectCode = "SRMSG", length = 5) +public interface SqsExceptions { + + SqsExceptions ex = Messages.getBundle(SqsExceptions.class); + + @Message(id = 19100, value = "Unable to build Pulsar client") + IllegalStateException illegalStateUnableToBuildClient(@Cause Throwable t); + + @Message(id = 19101, value = "Unable to build Pulsar consumer") + IllegalStateException illegalStateUnableToBuildConsumer(@Cause Throwable t); + + @Message(id = 19102, value = "Unable to build Pulsar producer") + IllegalStateException illegalStateUnableToBuildProducer(@Cause Throwable t); + + @Message(id = 19103, value = "Expecting downstream to consume without back-pressure") + IllegalStateException illegalStateConsumeWithoutBackPressure(); + + @Message(id = 19104, value = "Only one subscriber allowed") + IllegalStateException illegalStateOnlyOneSubscriber(); +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java new file mode 100644 index 0000000000..dec83ca17b --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java @@ -0,0 +1,18 @@ +package io.smallrye.reactive.messaging.aws.sqs.i18n; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; + +@MessageLogger(projectCode = "SRMSG", length = 5) +public interface SqsLogging extends BasicLogger { + + SqsLogging log = Logger.getMessageLogger(SqsLogging.class, "io.smallrye.reactive.messaging.aws.sqs"); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 19002, value = "Unable to close Sqs client") + void unableToCloseClient(@Cause Throwable t); +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java new file mode 100644 index 0000000000..9cfd5e87a8 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java @@ -0,0 +1,27 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.providers.locals.ContextAwareMessage; + +public abstract class SqsMessage implements ContextAwareMessage { + + private Target target; + private final M metadata; + + public SqsMessage(M metadata) { + this.metadata = metadata; + } + + public Target getTarget() { + return target; + } + + public SqsMessage withTarget(Target target) { + this.target = target; + return this; + } + + public M getSqsMetadata() { + return metadata; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java new file mode 100644 index 0000000000..f8d5a71d9d --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java @@ -0,0 +1,47 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +public abstract class SqsMessageMetadata { + + private String queue; + private String queueOwnerAWSAccountId; + private String conversationId; + + + /** + * Get the name of the queue + * + * @return the queue name + */ + public String getQueue() { + return queue; + } + + public SqsMessageMetadata withQueue(String queue) { + this.queue = queue; + return this; + } + + /** + * During queue name resolving it is possible to overwrite the AWS account id. If not specified the + * AWS accounts id from the provided client credentials are used + * + * @return overwritten AWS account id + */ + public String getQueueOwnerAWSAccountId() { + return queueOwnerAWSAccountId; + } + + public SqsMessageMetadata withQueueOwnerAWSAccountId(String queueOwnerAWSAccountId) { + this.queueOwnerAWSAccountId = queueOwnerAWSAccountId; + return this; + } + + public String getConversationId() { + return conversationId; + } + + public SqsMessageMetadata withConversationId(String conversationId) { + this.conversationId = conversationId; + return this; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java new file mode 100644 index 0000000000..fd28a8faf1 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java @@ -0,0 +1,43 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +import org.eclipse.microprofile.reactive.messaging.Message; + +import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import java.util.function.Supplier; + +public class SqsOutgoingMessage extends SqsMessage { + + private final T payload; + private final Supplier> ack; + private final Function> nack; + + public SqsOutgoingMessage(final T payload, final SqsOutgoingMessageMetadata metadata, Supplier> ack, + Function> nack) { + super(metadata); + this.payload = payload; + this.ack = ack; + this.nack = nack; + } + + @Override + public T getPayload() { + return payload; + } + + @Override + public Supplier> getAck() { + return this.ack; + } + + @Override + public Function> getNack() { + return this.nack; + } + + public static SqsOutgoingMessage from(Message message) { + return new SqsOutgoingMessage<>(message.getPayload(), message.getMetadata(SqsOutgoingMessageMetadata.class) + .orElseGet(SqsOutgoingMessageMetadata::new), message.getAck(), message.getNack()); + } + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java new file mode 100644 index 0000000000..ba24269f63 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java @@ -0,0 +1,5 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +public class SqsOutgoingMessageMetadata extends SqsMessageMetadata { + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java new file mode 100644 index 0000000000..cbb42640ed --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java @@ -0,0 +1,24 @@ +package io.smallrye.reactive.messaging.aws.sqs.tracing; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; + +public enum SqsAttributesExtractor implements AttributesExtractor { + INSTANCE; + + @Override + public void onStart(final AttributesBuilder attributes, final Context parentContext, final SqsTrace request) { + + } + + @Override + public void onEnd( + final AttributesBuilder attributes, + final Context context, + final SqsTrace request, + final Void response, + final Throwable error) { + + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java new file mode 100644 index 0000000000..9642f20399 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java @@ -0,0 +1,28 @@ +package io.smallrye.reactive.messaging.aws.sqs.tracing; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessageOperation; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; + +import static io.smallrye.reactive.messaging.tracing.TracingUtils.INSTRUMENTATION_NAME; + +public class SqsInstrumenter { + + public static final Instrumenter SQS_OUTGOING_INSTRUMENTER; + + static { + final InstrumenterBuilder instrumenterBuilder = Instrumenter.builder(GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, MessagingSpanNameExtractor.create( + SqsMessagingAttributesGetter.INSTANCE, MessageOperation.PUBLISH)); + + SQS_OUTGOING_INSTRUMENTER = instrumenterBuilder + .addAttributesExtractor(MessagingAttributesExtractor.create( + SqsMessagingAttributesGetter.INSTANCE, MessageOperation.PUBLISH)) + .addAttributesExtractor(SqsAttributesExtractor.INSTANCE) + .buildProducerInstrumenter(SqsTraceTextMapSetter.INSTANCE); + } + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java new file mode 100644 index 0000000000..59e01c64bf --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java @@ -0,0 +1,54 @@ +package io.smallrye.reactive.messaging.aws.sqs.tracing; + +import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesGetter; +import io.opentelemetry.instrumentation.api.instrumenter.network.NetworkAttributesGetter; + +public enum SqsMessagingAttributesGetter implements MessagingAttributesGetter, + NetworkAttributesGetter { + INSTANCE; + + @Override + public String getSystem(SqsTrace request) { + return "AmazonSQS"; + } + + @Override + public String getDestination(SqsTrace request) { + return request.getQueue(); + } + + @Override + public String getNetworkProtocolName(SqsTrace request, Void response) { + return "sqs"; + } + + @Override + public String getNetworkProtocolVersion(SqsTrace request, Void response) { + return "2012-11-05"; + } + + @Override + public boolean isTemporaryDestination(SqsTrace request) { + return false; + } + + @Override + public String getConversationId(SqsTrace request) { + return request.getConversationId(); + } + + @Override + public Long getMessagePayloadSize(SqsTrace request) { + return request.getMessagePayloadSize(); + } + + @Override + public Long getMessagePayloadCompressedSize(SqsTrace request) { + return null; + } + + @Override + public String getMessageId(SqsTrace request, Void response) { + return request.getMessageId(); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java new file mode 100644 index 0000000000..5d04ec380f --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java @@ -0,0 +1,44 @@ +package io.smallrye.reactive.messaging.aws.sqs.tracing; + +public class SqsTrace { + private String queue; + private String conversationId; + private String messageId; + private Long messagePayloadSize; + + public String getQueue() { + return queue; + } + + public SqsTrace withQueue(String queue) { + this.queue = queue; + return this; + } + + public String getConversationId() { + return conversationId; + } + + public SqsTrace withConversationId(String conversationId) { + this.conversationId = conversationId; + return this; + } + + public String getMessageId() { + return messageId; + } + + public SqsTrace withMessageId(String messageId) { + this.messageId = messageId; + return this; + } + + public Long getMessagePayloadSize() { + return messagePayloadSize; + } + + public SqsTrace withMessagePayloadSize(Long messagePayloadSize) { + this.messagePayloadSize = messagePayloadSize; + return this; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java new file mode 100644 index 0000000000..7ba28f5f91 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java @@ -0,0 +1,14 @@ +package io.smallrye.reactive.messaging.aws.sqs.tracing; + +import io.opentelemetry.context.propagation.TextMapSetter; + +public enum SqsTraceTextMapSetter implements TextMapSetter { + INSTANCE; + + @Override + public void set(SqsTrace carrier, String key, String value) { + if (carrier != null) { + + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java new file mode 100644 index 0000000000..cc629d3d66 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java @@ -0,0 +1,16 @@ +package io.smallrye.reactive.messaging.aws.sqs.util; + +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; +import io.smallrye.reactive.messaging.json.JsonMapping; + +public class Helper { + + public static String serialize(SqsMessage message, JsonMapping jsonMapping) { + if (message.getPayload() instanceof String) { + return (String) message.getPayload(); + } else { + return jsonMapping.toJson(message.getPayload()); + } + } +} diff --git a/smallrye-reactive-messaging-otel/src/main/java/io/smallrye/reactive/messaging/tracing/TracingUtils.java b/smallrye-reactive-messaging-otel/src/main/java/io/smallrye/reactive/messaging/tracing/TracingUtils.java index 74bc791822..ee1604a106 100644 --- a/smallrye-reactive-messaging-otel/src/main/java/io/smallrye/reactive/messaging/tracing/TracingUtils.java +++ b/smallrye-reactive-messaging-otel/src/main/java/io/smallrye/reactive/messaging/tracing/TracingUtils.java @@ -12,6 +12,8 @@ public class TracingUtils { + public static final String INSTRUMENTATION_NAME = "io.smallrye.reactive.messaging"; + private TracingUtils() { } From 03f837cd9744e0bfb14be6701c67da0ae89c50b9 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 09:54:12 +0200 Subject: [PATCH 02/14] Prepare create queue if not exists --- .../messaging/aws/sqs/SqsConnector.java | 51 +++++----- .../messaging/aws/sqs/SqsOutgoingChannel.java | 19 ++-- .../TargetCache.java => TargetResolver.java} | 16 ++-- .../aws/sqs/action/CreateQueueAction.java | 95 +++++++++++++++++++ .../aws/sqs/action/GetQueueUrlAction.java | 14 +-- .../sqs/action/SendBatchMessageAction.java | 79 ++++++++------- .../aws/sqs/action/SendMessageAction.java | 26 +---- .../aws/sqs/client/SqsClientHolder.java | 12 +-- .../sqs/message/SqsCreateQueueMetadata.java | 30 ++++++ .../aws/sqs/message/SqsMessageMetadata.java | 24 +++++ .../aws/sqs/message/SqsOutgoingMessage.java | 6 +- .../aws/sqs/tracing/SqsInstrumenter.java | 4 +- 12 files changed, 261 insertions(+), 115 deletions(-) rename smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/{cache/TargetCache.java => TargetResolver.java} (72%) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index a2bad4c407..09fac81a3b 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -1,12 +1,17 @@ package io.smallrye.reactive.messaging.aws.sqs; -import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; -import io.smallrye.reactive.messaging.aws.sqs.cache.TargetCache; -import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.connector.InboundConnector; -import io.smallrye.reactive.messaging.connector.OutboundConnector; -import io.smallrye.reactive.messaging.health.HealthReporter; -import io.smallrye.reactive.messaging.json.JsonMapping; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; +import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Flow; + import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; @@ -15,30 +20,31 @@ import jakarta.enterprise.event.Reception; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; + import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.reactive.messaging.Message; import org.eclipse.microprofile.reactive.messaging.spi.Connector; + +import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.connector.InboundConnector; +import io.smallrye.reactive.messaging.connector.OutboundConnector; +import io.smallrye.reactive.messaging.health.HealthReporter; +import io.smallrye.reactive.messaging.json.JsonMapping; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.SqsException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Flow; - -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; -import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; - @ApplicationScoped @Connector(SqsConnector.CONNECTOR_NAME) // common @ConnectorAttribute(name = "queue", type = "string", direction = INCOMING_AND_OUTGOING, description = "Set the SQS queue. If not set, the channel name is used") @ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") @ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") +@ConnectorAttribute(name = "create-queue.dlq.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") +@ConnectorAttribute(name = "create-queue.dlq.prefix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") +@ConnectorAttribute(name = "create-queue.dlq.suffix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") +@ConnectorAttribute(name = "create-queue.dlq.max-receive-count", type = "int", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") // outgoing @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") @@ -90,7 +96,8 @@ public Flow.Subscriber> getSubscriber(Config config) { clientsByChannel.put(oc.getChannel(), client); try { - SqsOutgoingChannel channel = new SqsOutgoingChannel(new SqsClientHolder<>(client, oc, jsonMapping, new TargetCache())); + SqsOutgoingChannel channel = new SqsOutgoingChannel( + new SqsClientHolder<>(client, oc, jsonMapping, new TargetResolver())); outgoingChannels.add(channel); return channel.getSubscriber(); } catch (SqsException e) { @@ -99,9 +106,7 @@ public Flow.Subscriber> getSubscriber(Config config) { } public void terminate( - @Observes(notifyObserver = Reception.IF_EXISTS) - @Priority(50) - @BeforeDestroyed(ApplicationScoped.class) Object event) { + @Observes(notifyObserver = Reception.IF_EXISTS) @Priority(50) @BeforeDestroyed(ApplicationScoped.class) Object event) { // incomingChannels.forEach(PulsarIncomingChannel::close); outgoingChannels.forEach(SqsOutgoingChannel::close); for (SqsAsyncClient client : clients.values()) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index cca9ae3487..7e85c2566e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -1,5 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import org.eclipse.microprofile.reactive.messaging.Message; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.tuples.Tuple2; @@ -11,13 +19,6 @@ import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; import io.smallrye.reactive.messaging.tracing.TracingUtils; -import org.eclipse.microprofile.reactive.messaging.Message; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Flow; - -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; public class SqsOutgoingChannel { @@ -95,8 +96,8 @@ private void tracing(SqsOutgoingMessage message) { // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. // .withMessageId("") .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; + // We do not set payload size. This would require to calculate it, which is less performant. + ; TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java similarity index 72% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java index 50ad3ae685..2d7dff6226 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/cache/TargetCache.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java @@ -1,17 +1,17 @@ -package io.smallrye.reactive.messaging.aws.sqs.cache; +package io.smallrye.reactive.messaging.aws.sqs; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import io.smallrye.mutiny.Uni; -import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; -import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.aws.sqs.action.CreateQueueAction; import io.smallrye.reactive.messaging.aws.sqs.action.GetQueueUrlAction; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; +import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class TargetCache { +public class TargetResolver { private final Map> CACHE = new ConcurrentHashMap<>(); @@ -22,6 +22,8 @@ public Uni getTarget( return CACHE.computeIfAbsent( clientHolder.getConfig().getQueue().orElse(config.getChannel()), key -> GetQueueUrlAction.resolveQueueUrl(clientHolder, message) + .onFailure(QueueDoesNotExistException.class) + .call(() -> CreateQueueAction.createQueue(clientHolder, message)) .onItem().transform(url -> new Target(key, url)) .memoize().indefinitely()); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java new file mode 100644 index 0000000000..6f7d930000 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -0,0 +1,95 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsCreateQueueMetadata; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.CreateQueueResponse; +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; + +import java.util.Map; + +/** + * AWS + * Documentation + */ +public class CreateQueueAction { + + public static Uni createQueue( + SqsClientHolder clientHolder, SqsMessage message) { + SqsConnectorCommonConfiguration config = clientHolder.getConfig(); + + if (!config.getCreateQueueEnabled()) { + return Uni.createFrom().nullItem(); + } + + String queueName = message.getTarget().getTargetName(); + Uni> uni = Uni.createFrom().nullItem(); + M metadata = message.getSqsMetadata(); + + if (config.getCreateQueueDlqEnabled()) { + final SqsCreateQueueMetadata createQueueDlqMetadata = metadata.getCreateQueueDlqMetadata(); + uni = uni.onItem().transformToUni(ignore -> createDlq( + clientHolder, queueName, config, + createQueueDlqMetadata.getAttributes(), createQueueDlqMetadata.getTags() + )); + } + + + CreateQueueRequest.builder().queueName(queueName); + + return uni.replaceWith(""); + } + + private static Uni> createDlq(SqsClientHolder clientHolder, String queueName, + SqsConnectorCommonConfiguration config, + Map attributes, + Map tags) { + + Uni uni = createQueue(clientHolder, + config.getCreateQueueDlqPrefix() + queueName + config.getCreateQueueDlqSuffix(), + Map.of(), Map.of()); + + return uni.onItem().transformToUni(response -> getQueueArn(clientHolder, response)) + .onItem().transform(arn -> Map.of( + QueueAttributeName.REDRIVE_POLICY, createRedrivePolicy(clientHolder, config, arn))); + } + + private static String createRedrivePolicy(SqsClientHolder clientHolder, SqsConnectorCommonConfiguration config, + String arn) { + return clientHolder.getJsonMapping().toJson(Map.of( + "deadLetterTargetArn", arn, + "maxReceiveCount", config.getCreateQueueDlqMaxReceiveCount() + )); + } + + private static Uni createQueue(SqsClientHolder clientHolder, String queueName, + Map attributes, + Map tags) { + + CreateQueueRequest request = CreateQueueRequest.builder() + .queueName(queueName) + .attributes(attributes) + .tags(tags) + .build(); + + return Uni.createFrom().completionStage(clientHolder.getClient().createQueue(request)) + // TODO: Log exception and success + .onFailure().invoke(e -> { + }); + } + + private static Uni getQueueArn(SqsClientHolder clientHolder, CreateQueueResponse response) { + + final GetQueueAttributesRequest request = GetQueueAttributesRequest.builder().queueUrl(response.queueUrl()) + .attributeNames(QueueAttributeName.QUEUE_ARN).build(); + + return Uni.createFrom().completionStage(clientHolder.getClient().getQueueAttributes(request)) + .onItem().transform(r -> r.attributes().get(QueueAttributeName.QUEUE_ARN)); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java index 10cc6b37fd..dcb4f3c3a1 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java @@ -11,15 +11,15 @@ public class GetQueueUrlAction { public static Uni resolveQueueUrl( - final SqsClientHolder clientHolder, final SqsMessage message) { - final SqsConnectorCommonConfiguration config = clientHolder.getConfig(); - final SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); + SqsClientHolder clientHolder, SqsMessage message) { + SqsConnectorCommonConfiguration config = clientHolder.getConfig(); + SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); return Uni.createFrom().completionStage( - clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() - .queueName(config.getQueue().orElse(config.getChannel())) - .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) - .build())) + clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() + .queueName(config.getQueue().orElse(config.getChannel())) + .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) + .build())) .onItem().transform(GetQueueUrlResponse::queueUrl); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java index c2fbd16e08..12f389a0df 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java @@ -1,5 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; @@ -12,25 +20,44 @@ import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; - public class SendBatchMessageAction { public static Uni sendMessage( - final SqsClientHolder clientHolder, - Target target, final List> messages) { - final Map entryMap = new HashMap<>(); - final Map> messageMap = new HashMap<>(); + SqsClientHolder clientHolder, + Target target, List> messages) { + // We need to remember the correlation between messageId and outgoing message. + Map> messageMap = new HashMap<>(); + + SendMessageBatchRequest request = createRequest(clientHolder, target, messages, messageMap); + + // TODO: logging + Uni uni = Uni.createFrom().completionStage(clientHolder.getClient().sendMessageBatch(request)) + .onItem().transformToUni(response -> handleResponse(response, messageMap)); + + // TODO: configurable retry. Retry complete batch in case the complete HTTP request fails + // but what to do in case just specific messages fails? In theory most efficient would be to retry + // them by adding them back to the stream. This seems very difficult. Another option is to do the configured + // retries for the failed once immediately until all are successful or max retries reached. + if (true) { + uni = uni.onFailure().retry() + .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) + .atMost(3); + } + + // TODO: micrometer? Failure and Success? + + return uni; + } + + private static SendMessageBatchRequest createRequest( + SqsClientHolder clientHolder, + Target target, List> messages, + Map> messageMap) { + Map entryMap = new HashMap<>(); messages.forEach(msg -> { - final String payload = Helper.serialize(msg, clientHolder.getJsonMapping()); - final String id = UUID.randomUUID().toString(); + String payload = Helper.serialize(msg, clientHolder.getJsonMapping()); + String id = UUID.randomUUID().toString(); final SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() // in batching we need to generate the id of a message for every entry. @@ -47,39 +74,21 @@ public static Uni sendMessage( entryMap.put(id, entry); }); - final SendMessageBatchRequest request = SendMessageBatchRequest.builder() + return SendMessageBatchRequest.builder() .queueUrl(target.getTargetUrl()) .entries(entryMap.values()) .build(); - - // TODO: logging - Uni uni = Uni.createFrom().completionStage(clientHolder.getClient().sendMessageBatch(request)) - .onItem().transformToUni(response -> handleResponse(response, messageMap)); - - // TODO: configurable retry. Retry complete batch in case the complete HTTP request fails - // but what to do in case just specific messages fails? In theory most efficient would be to retry - // them by adding them back to the stream. This seems very difficult. Another option is to do the configured - // retries for the failed once immediately until all are successful or max retries reached. - if (true) { - uni = uni.onFailure().retry() - .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) - .atMost(3); - } - - // TODO: micrometer? Failure and Success? - - return uni; } private static Uni handleResponse( SendMessageBatchResponse response, Map> messageMap) { - final Uni successful = Multi.createFrom().iterable(response.successful()) + Uni successful = Multi.createFrom().iterable(response.successful()) .onItem().call(result -> { OutgoingMessageMetadata.setResultOnMessage(messageMap.get(result.id()), result); return Uni.createFrom().completionStage(messageMap.get(result.id()).ack()); }).collect().last().replaceWithVoid(); - final Uni failed = Multi.createFrom().iterable(response.failed()) + Uni failed = Multi.createFrom().iterable(response.failed()) .onItem().call(result -> { OutgoingMessageMetadata.setResultOnMessage(messageMap.get(result.id()), result); return Uni.createFrom().completionStage(messageMap.get(result.id()) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java index af5fe264a5..9282db11dd 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -1,42 +1,22 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import java.time.Duration; + import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; -import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.aws.sqs.util.Helper; -import io.smallrye.reactive.messaging.tracing.TracingUtils; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; -import java.time.Duration; - -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; - public class SendMessageAction { public static Uni sendMessage( final SqsClientHolder clientHolder, final SqsOutgoingMessage message) { String payload = Helper.serialize(message, clientHolder.getJsonMapping()); - if (clientHolder.getConfig().getTracingEnabled()) { - SqsTrace trace = new SqsTrace() - .withQueue(message.getTarget().getTargetName()) - // TODO: Cannot set messageId. It is provided in response. - // The intstrumenter documentation says: - // Call shouldStart(Context, Object) and do not proceed if it returns false. - // Call start(Context, Object) at the beginning of a request. - // Call end(Context, Object, Object, Throwable) at the end of a request. - // TracingUtils violates this, because end is called immediately and not at the end of a request. - // .withMessageId("") - .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; - TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); - } - - final SendMessageRequest request = SendMessageRequest.builder() + SendMessageRequest request = SendMessageRequest.builder() .queueUrl(message.getTarget().getTargetUrl()) .messageAttributes(null) .messageGroupId(null) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java index ddf8fd8b80..a02a33bb2e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -1,7 +1,7 @@ package io.smallrye.reactive.messaging.aws.sqs.client; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; -import io.smallrye.reactive.messaging.aws.sqs.cache.TargetCache; +import io.smallrye.reactive.messaging.aws.sqs.TargetResolver; import io.smallrye.reactive.messaging.json.JsonMapping; import software.amazon.awssdk.services.sqs.SqsAsyncClient; @@ -9,13 +9,13 @@ public class SqsClientHolder { private final SqsAsyncClient client; private final C config; private final JsonMapping jsonMapping; - private final TargetCache targetCache; + private final TargetResolver targetResolver; - public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, TargetCache targetCache) { + public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, TargetResolver targetResolver) { this.client = client; this.config = config; this.jsonMapping = jsonMapping; - this.targetCache = targetCache; + this.targetResolver = targetResolver; } public SqsAsyncClient getClient() { @@ -30,7 +30,7 @@ public JsonMapping getJsonMapping() { return jsonMapping; } - public TargetCache getTargetCache() { - return targetCache; + public TargetResolver getTargetCache() { + return targetResolver; } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java new file mode 100644 index 0000000000..2d09f1a7cf --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java @@ -0,0 +1,30 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; + +import java.util.HashMap; +import java.util.Map; + +public class SqsCreateQueueMetadata { + + private Map attributes = new HashMap<>(); + private Map tags = new HashMap<>(); + + public Map getAttributes() { + return attributes; + } + + public SqsCreateQueueMetadata withAttributes(Map attributes) { + this.attributes = attributes; + return this; + } + + public Map getTags() { + return tags; + } + + public SqsCreateQueueMetadata withTags(Map tags) { + this.tags = tags; + return this; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java index f8d5a71d9d..256d36de4a 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java @@ -5,7 +5,13 @@ public abstract class SqsMessageMetadata { private String queue; private String queueOwnerAWSAccountId; private String conversationId; + private SqsCreateQueueMetadata createQueueMetadata; + private SqsCreateQueueMetadata createQueueDlqMetadata; + public SqsMessageMetadata() { + createQueueMetadata = new SqsCreateQueueMetadata(); + createQueueDlqMetadata = new SqsCreateQueueMetadata(); + } /** * Get the name of the queue @@ -44,4 +50,22 @@ public SqsMessageMetadata withConversationId(String conversationId) { this.conversationId = conversationId; return this; } + + public SqsCreateQueueMetadata getCreateQueueMetadata() { + return createQueueMetadata; + } + + public SqsMessageMetadata withCreateQueueMetadata(SqsCreateQueueMetadata createQueueMetadata) { + this.createQueueMetadata = createQueueMetadata; + return this; + } + + public SqsCreateQueueMetadata getCreateQueueDlqMetadata() { + return createQueueDlqMetadata; + } + + public SqsMessageMetadata withCreateQueueDlqMetadata(SqsCreateQueueMetadata createQueueDlqMetadata) { + this.createQueueDlqMetadata = createQueueDlqMetadata; + return this; + } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java index fd28a8faf1..b8fe067888 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java @@ -1,11 +1,11 @@ package io.smallrye.reactive.messaging.aws.sqs.message; -import org.eclipse.microprofile.reactive.messaging.Message; - import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.function.Supplier; +import org.eclipse.microprofile.reactive.messaging.Message; + public class SqsOutgoingMessage extends SqsMessage { private final T payload; @@ -13,7 +13,7 @@ public class SqsOutgoingMessage extends SqsMessage> nack; public SqsOutgoingMessage(final T payload, final SqsOutgoingMessageMetadata metadata, Supplier> ack, - Function> nack) { + Function> nack) { super(metadata); this.payload = payload; this.ack = ack; diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java index 9642f20399..bfd2301805 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java @@ -1,5 +1,7 @@ package io.smallrye.reactive.messaging.aws.sqs.tracing; +import static io.smallrye.reactive.messaging.tracing.TracingUtils.INSTRUMENTATION_NAME; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; @@ -7,8 +9,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.messaging.MessagingSpanNameExtractor; -import static io.smallrye.reactive.messaging.tracing.TracingUtils.INSTRUMENTATION_NAME; - public class SqsInstrumenter { public static final Instrumenter SQS_OUTGOING_INSTRUMENTER; From 8d2f1529d4eff8b7d1c7a271734ebd208bbd43a4 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 09:54:55 +0200 Subject: [PATCH 03/14] renaming --- smallrye-reactive-messaging-aws/pom.xml | 2 +- .../pom.xml | 2 +- .../java/io/smallrye/reactive/messaging/aws/sqs/Clients.java | 0 .../io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java | 0 .../smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java | 0 .../java/io/smallrye/reactive/messaging/aws/sqs/Target.java | 0 .../io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java | 0 .../reactive/messaging/aws/sqs/action/CreateQueueAction.java | 0 .../reactive/messaging/aws/sqs/action/GetQueueUrlAction.java | 0 .../messaging/aws/sqs/action/SendBatchMessageAction.java | 0 .../reactive/messaging/aws/sqs/action/SendMessageAction.java | 0 .../reactive/messaging/aws/sqs/client/SqsClientFactory.java | 0 .../reactive/messaging/aws/sqs/client/SqsClientHolder.java | 0 .../reactive/messaging/aws/sqs/config/ConfigResolver.java | 0 .../smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java | 0 .../io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java | 0 .../messaging/aws/sqs/message/SqsCreateQueueMetadata.java | 0 .../smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java | 0 .../reactive/messaging/aws/sqs/message/SqsMessageMetadata.java | 0 .../reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java | 0 .../messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java | 0 .../messaging/aws/sqs/tracing/SqsAttributesExtractor.java | 0 .../reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java | 0 .../messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java | 0 .../smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java | 0 .../messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java | 0 .../io/smallrye/reactive/messaging/aws/sqs/util/Helper.java | 0 27 files changed, 2 insertions(+), 2 deletions(-) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/pom.xml (93%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java (100%) rename smallrye-reactive-messaging-aws/{smallrye-reactive-messaging-sqs => smallrye-reactive-messaging-aws-sqs}/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java (100%) diff --git a/smallrye-reactive-messaging-aws/pom.xml b/smallrye-reactive-messaging-aws/pom.xml index 22e69b760f..67451d1e0f 100644 --- a/smallrye-reactive-messaging-aws/pom.xml +++ b/smallrye-reactive-messaging-aws/pom.xml @@ -21,7 +21,7 @@ - smallrye-reactive-messaging-sqs + smallrye-reactive-messaging-aws-sqs diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml similarity index 93% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml index 3cce937233..567c0b08d1 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml @@ -9,7 +9,7 @@ 4.11.0-SNAPSHOT - smallrye-reactive-messaging-sqs + smallrye-reactive-messaging-aws-sqs SmallRye Reactive Messaging : Connector :: AWS SQS diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsAttributesExtractor.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsInstrumenter.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsMessagingAttributesGetter.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTrace.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/tracing/SqsTraceTextMapSetter.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java similarity index 100% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java From 5feb48ac0b8faffa7936aeb5d68c62d56456a523 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:07:43 +0200 Subject: [PATCH 04/14] create queue if not exists --- .../aws/sqs/action/CreateQueueAction.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java index 6f7d930000..7a66bc6a87 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -11,6 +11,7 @@ import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; import software.amazon.awssdk.services.sqs.model.QueueAttributeName; +import java.util.HashMap; import java.util.Map; /** @@ -29,21 +30,26 @@ public static Uni createQueue( } String queueName = message.getTarget().getTargetName(); - Uni> uni = Uni.createFrom().nullItem(); + Uni> uni = Uni.createFrom().item(Map.of()); M metadata = message.getSqsMetadata(); if (config.getCreateQueueDlqEnabled()) { - final SqsCreateQueueMetadata createQueueDlqMetadata = metadata.getCreateQueueDlqMetadata(); + SqsCreateQueueMetadata createQueueDlqMetadata = metadata.getCreateQueueDlqMetadata(); uni = uni.onItem().transformToUni(ignore -> createDlq( clientHolder, queueName, config, createQueueDlqMetadata.getAttributes(), createQueueDlqMetadata.getTags() )); } + return uni.onItem().transformToUni(preparedAttributes -> { + SqsCreateQueueMetadata createQueueMetadata = metadata.getCreateQueueMetadata(); - CreateQueueRequest.builder().queueName(queueName); + HashMap attributes = new HashMap<>(); + attributes.putAll(preparedAttributes); + attributes.putAll(createQueueMetadata.getAttributes()); - return uni.replaceWith(""); + return createQueue(clientHolder, queueName, attributes, createQueueMetadata.getTags()); + }).onItem().transform(CreateQueueResponse::queueUrl); } private static Uni> createDlq(SqsClientHolder clientHolder, String queueName, @@ -53,7 +59,7 @@ private static Uni> createDlq(SqsClientHolder Uni uni = createQueue(clientHolder, config.getCreateQueueDlqPrefix() + queueName + config.getCreateQueueDlqSuffix(), - Map.of(), Map.of()); + attributes, tags); return uni.onItem().transformToUni(response -> getQueueArn(clientHolder, response)) .onItem().transform(arn -> Map.of( From c8d562b4c190fbccdf54abeabfc689c011938371 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 10:11:01 +0200 Subject: [PATCH 05/14] renaming and cleanup --- .../reactive/messaging/aws/sqs/Clients.java | 5 --- .../messaging/aws/sqs/SqsConnector.java | 40 +++++++++---------- .../messaging/aws/sqs/SqsOutgoingChannel.java | 25 ++++++------ .../messaging/aws/sqs/SqsSubscribe.java | 8 ++++ .../aws/sqs/{Target.java => SqsTarget.java} | 6 +-- ...etResolver.java => SqsTargetResolver.java} | 14 +++---- .../aws/sqs/action/CreateQueueAction.java | 4 +- .../aws/sqs/action/GetQueueUrlAction.java | 11 +++-- .../aws/sqs/action/ReceiveMessageAction.java | 8 ++++ .../aws/sqs/action/SendMessageAction.java | 7 +++- ...ction.java => SendMessageBatchAction.java} | 27 +++++++------ .../aws/sqs/client/SqsClientHolder.java | 8 ++-- .../messaging/aws/sqs/message/SqsMessage.java | 8 ++-- 13 files changed, 93 insertions(+), 78 deletions(-) delete mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java rename smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/{Target.java => SqsTarget.java} (84%) rename smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/{TargetResolver.java => SqsTargetResolver.java} (81%) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java rename smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/{SendBatchMessageAction.java => SendMessageBatchAction.java} (92%) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java deleted file mode 100644 index f06f438835..0000000000 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Clients.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.smallrye.reactive.messaging.aws.sqs; - -public class Clients { - -} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index 09fac81a3b..fd629a9c5f 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -1,17 +1,11 @@ package io.smallrye.reactive.messaging.aws.sqs; -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; -import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Flow; - +import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.connector.InboundConnector; +import io.smallrye.reactive.messaging.connector.OutboundConnector; +import io.smallrye.reactive.messaging.health.HealthReporter; +import io.smallrye.reactive.messaging.json.JsonMapping; import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; @@ -20,20 +14,24 @@ import jakarta.enterprise.event.Reception; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; - import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.reactive.messaging.Message; import org.eclipse.microprofile.reactive.messaging.spi.Connector; - -import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; -import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.connector.InboundConnector; -import io.smallrye.reactive.messaging.connector.OutboundConnector; -import io.smallrye.reactive.messaging.health.HealthReporter; -import io.smallrye.reactive.messaging.json.JsonMapping; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.SqsException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Flow; + +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; +import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; + @ApplicationScoped @Connector(SqsConnector.CONNECTOR_NAME) // common @@ -97,7 +95,7 @@ public Flow.Subscriber> getSubscriber(Config config) { try { SqsOutgoingChannel channel = new SqsOutgoingChannel( - new SqsClientHolder<>(client, oc, jsonMapping, new TargetResolver())); + new SqsClientHolder<>(client, oc, jsonMapping, new SqsTargetResolver())); outgoingChannels.add(channel); return channel.getSubscriber(); } catch (SqsException e) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index 7e85c2566e..4c43683658 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -1,24 +1,23 @@ package io.smallrye.reactive.messaging.aws.sqs; -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Flow; - -import org.eclipse.microprofile.reactive.messaging.Message; - import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.tuples.Tuple2; -import io.smallrye.reactive.messaging.aws.sqs.action.SendBatchMessageAction; import io.smallrye.reactive.messaging.aws.sqs.action.SendMessageAction; +import io.smallrye.reactive.messaging.aws.sqs.action.SendMessageBatchAction; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; import io.smallrye.reactive.messaging.tracing.TracingUtils; +import org.eclipse.microprofile.reactive.messaging.Message; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; public class SqsOutgoingChannel { @@ -74,8 +73,8 @@ private Uni sendMessage(SqsOutgoingMessage message) { .onItem().transformToUni(target -> SendMessageAction.sendMessage(clientHolder, sqsMessage)); } - private Uni sendBatchMessage(Target target, List> messages) { - return SendBatchMessageAction.sendMessage(clientHolder, target, messages); + private Uni sendBatchMessage(SqsTarget target, List> messages) { + return SendMessageBatchAction.sendMessage(clientHolder, target, messages); } private void tracing(SqsOutgoingMessage message) { @@ -96,8 +95,8 @@ private void tracing(SqsOutgoingMessage message) { // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. // .withMessageId("") .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; + // We do not set payload size. This would require to calculate it, which is less performant. + ; TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java new file mode 100644 index 0000000000..f9cda2a2d1 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java @@ -0,0 +1,8 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +/** + * @author Christopher Holomek + * @since 01.10.2023 + */ +public class SqsSubscribe { +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTarget.java similarity index 84% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTarget.java index 756214d4d9..40059507a5 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/Target.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTarget.java @@ -2,12 +2,12 @@ import java.util.Objects; -public class Target { +public class SqsTarget { private final String targetName; private final String targetUrl; - public Target(String targetName, String targetUrl) { + public SqsTarget(String targetName, String targetUrl) { this.targetName = targetName; this.targetUrl = targetUrl; } @@ -26,7 +26,7 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - Target target = (Target) o; + SqsTarget target = (SqsTarget) o; return Objects.equals(targetUrl, target.targetUrl); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java similarity index 81% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java index 2d7dff6226..76cf62514e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/TargetResolver.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java @@ -1,8 +1,5 @@ package io.smallrye.reactive.messaging.aws.sqs; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.action.CreateQueueAction; import io.smallrye.reactive.messaging.aws.sqs.action.GetQueueUrlAction; @@ -11,11 +8,14 @@ import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; -public class TargetResolver { +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SqsTargetResolver { - private final Map> CACHE = new ConcurrentHashMap<>(); + private final Map> CACHE = new ConcurrentHashMap<>(); - public Uni getTarget( + public Uni getTarget( final SqsClientHolder clientHolder, final SqsMessage message) { final SqsConnectorCommonConfiguration config = clientHolder.getConfig(); @@ -24,7 +24,7 @@ public Uni getTarget( key -> GetQueueUrlAction.resolveQueueUrl(clientHolder, message) .onFailure(QueueDoesNotExistException.class) .call(() -> CreateQueueAction.createQueue(clientHolder, message)) - .onItem().transform(url -> new Target(key, url)) + .onItem().transform(url -> new SqsTarget(key, url)) .memoize().indefinitely()); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java index 7a66bc6a87..cff072f6b0 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -15,9 +15,7 @@ import java.util.Map; /** - * AWS - * Documentation + * AWS Documentation */ public class CreateQueueAction { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java index dcb4f3c3a1..887f061137 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java @@ -8,6 +8,9 @@ import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; +/** + * AWS Documentation + */ public class GetQueueUrlAction { public static Uni resolveQueueUrl( @@ -16,10 +19,10 @@ public static Uni resolveQueueUrl( SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); return Uni.createFrom().completionStage( - clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() - .queueName(config.getQueue().orElse(config.getChannel())) - .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) - .build())) + clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() + .queueName(config.getQueue().orElse(config.getChannel())) + .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) + .build())) .onItem().transform(GetQueueUrlResponse::queueUrl); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java new file mode 100644 index 0000000000..b93db3c2e7 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java @@ -0,0 +1,8 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +/** + * @author Christopher Holomek + * @since 01.10.2023 + */ +public class ReceiveMessageAction { +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java index 9282db11dd..7043901606 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -1,7 +1,5 @@ package io.smallrye.reactive.messaging.aws.sqs.action; -import java.time.Duration; - import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; @@ -10,6 +8,11 @@ import io.smallrye.reactive.messaging.aws.sqs.util.Helper; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; +import java.time.Duration; + +/** + * AWS Documentation + */ public class SendMessageAction { public static Uni sendMessage( diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java similarity index 92% rename from smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java rename to smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java index 12f389a0df..25ca7ae620 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendBatchMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java @@ -1,18 +1,10 @@ package io.smallrye.reactive.messaging.aws.sqs.action; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; - -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; -import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; import io.smallrye.reactive.messaging.aws.sqs.util.Helper; @@ -20,11 +12,22 @@ import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -public class SendBatchMessageAction { +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; + +/** + * AWS Documentation + */ +public class SendMessageBatchAction { public static Uni sendMessage( SqsClientHolder clientHolder, - Target target, List> messages) { + SqsTarget target, List> messages) { // We need to remember the correlation between messageId and outgoing message. Map> messageMap = new HashMap<>(); @@ -51,7 +54,7 @@ public static Uni sendMessage( private static SendMessageBatchRequest createRequest( SqsClientHolder clientHolder, - Target target, List> messages, + SqsTarget target, List> messages, Map> messageMap) { Map entryMap = new HashMap<>(); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java index a02a33bb2e..de77269239 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -1,7 +1,7 @@ package io.smallrye.reactive.messaging.aws.sqs.client; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; -import io.smallrye.reactive.messaging.aws.sqs.TargetResolver; +import io.smallrye.reactive.messaging.aws.sqs.SqsTargetResolver; import io.smallrye.reactive.messaging.json.JsonMapping; import software.amazon.awssdk.services.sqs.SqsAsyncClient; @@ -9,9 +9,9 @@ public class SqsClientHolder { private final SqsAsyncClient client; private final C config; private final JsonMapping jsonMapping; - private final TargetResolver targetResolver; + private final SqsTargetResolver targetResolver; - public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, TargetResolver targetResolver) { + public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, SqsTargetResolver targetResolver) { this.client = client; this.config = config; this.jsonMapping = jsonMapping; @@ -30,7 +30,7 @@ public JsonMapping getJsonMapping() { return jsonMapping; } - public TargetResolver getTargetCache() { + public SqsTargetResolver getTargetCache() { return targetResolver; } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java index 9cfd5e87a8..71902ea414 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java @@ -1,22 +1,22 @@ package io.smallrye.reactive.messaging.aws.sqs.message; -import io.smallrye.reactive.messaging.aws.sqs.Target; +import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.providers.locals.ContextAwareMessage; public abstract class SqsMessage implements ContextAwareMessage { - private Target target; + private SqsTarget target; private final M metadata; public SqsMessage(M metadata) { this.metadata = metadata; } - public Target getTarget() { + public SqsTarget getTarget() { return target; } - public SqsMessage withTarget(Target target) { + public SqsMessage withTarget(SqsTarget target) { this.target = target; return this; } From d07c282c5fd8dae270dc6dcc6515263c66d8b173 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 13:17:20 +0200 Subject: [PATCH 06/14] add core module, refactor serialization --- smallrye-reactive-messaging-aws/pom.xml | 1 + .../pom.xml | 20 +++++ .../messaging/aws/client/ClientHolder.java | 42 +++++++++++ .../messaging/aws/i18n/AwsExceptions.java | 19 +++++ .../aws/serialization/Deserializer.java | 17 +++++ .../serialization/SerializationResolver.java | 73 +++++++++++++++++++ .../aws/serialization/Serializer.java | 17 +++++ .../pom.xml | 31 ++++++++ .../messaging/aws/sqs/SqsChannel.java | 18 +++++ .../messaging/aws/sqs/SqsConnector.java | 63 ++++++++++++---- .../messaging/aws/sqs/SqsIncomingChannel.java | 50 +++++++++++++ .../messaging/aws/sqs/SqsOutgoingChannel.java | 21 +++--- .../messaging/aws/sqs/SqsSubscribe.java | 8 -- .../messaging/aws/sqs/SqsTargetResolver.java | 6 +- .../aws/sqs/action/CreateQueueAction.java | 38 +++++----- .../aws/sqs/action/GetQueueUrlAction.java | 8 +- .../aws/sqs/action/ReceiveMessageAction.java | 23 +++++- .../aws/sqs/action/SendMessageAction.java | 8 +- .../sqs/action/SendMessageBatchAction.java | 22 +++--- .../aws/sqs/client/SqsClientFactory.java | 57 +++++++++++++-- .../aws/sqs/client/SqsClientHolder.java | 29 ++------ .../aws/sqs/config/ConfigResolver.java | 4 - .../sqs/message/SqsCreateQueueMetadata.java | 4 +- .../messaging/aws/sqs/util/Helper.java | 16 ---- 24 files changed, 473 insertions(+), 122 deletions(-) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/client/ClientHolder.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/i18n/AwsExceptions.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java delete mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java delete mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java delete mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java diff --git a/smallrye-reactive-messaging-aws/pom.xml b/smallrye-reactive-messaging-aws/pom.xml index 67451d1e0f..2f99c00976 100644 --- a/smallrye-reactive-messaging-aws/pom.xml +++ b/smallrye-reactive-messaging-aws/pom.xml @@ -22,6 +22,7 @@ smallrye-reactive-messaging-aws-sqs + smallrye-reactive-messaging-aws-core diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml new file mode 100644 index 0000000000..db61529b5f --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + io.smallrye.reactive + smallrye-reactive-messaging-aws + 4.11.0-SNAPSHOT + + + smallrye-reactive-messaging-aws-core + + SmallRye Reactive Messaging : Connector :: AWS Core + + + + + + diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/client/ClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/client/ClientHolder.java new file mode 100644 index 0000000000..0004b9009e --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/client/ClientHolder.java @@ -0,0 +1,42 @@ +package io.smallrye.reactive.messaging.aws.client; + +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; +import io.smallrye.reactive.messaging.aws.serialization.Serializer; +import io.vertx.mutiny.core.Vertx; + +public class ClientHolder { + + private final CLIENT client; + private final Vertx vertx; + private final CONFIG config; + private final Serializer serializer; + private final Deserializer deserializer; + + public ClientHolder(CLIENT client, Vertx vertx, CONFIG config, Serializer serializer, Deserializer deserializer) { + this.client = client; + this.vertx = vertx; + this.config = config; + this.serializer = serializer; + this.deserializer = deserializer; + } + + public CLIENT getClient() { + return client; + } + + public Vertx getVertx() { + return vertx; + } + + public CONFIG getConfig() { + return config; + } + + public Serializer getSerializer() { + return serializer; + } + + public Deserializer getDeserializer() { + return deserializer; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/i18n/AwsExceptions.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/i18n/AwsExceptions.java new file mode 100644 index 0000000000..46522e0dfc --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/i18n/AwsExceptions.java @@ -0,0 +1,19 @@ +package io.smallrye.reactive.messaging.aws.i18n; + +import jakarta.enterprise.inject.AmbiguousResolutionException; + +import org.jboss.logging.Messages; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageBundle; + +@MessageBundle(projectCode = "SRMSG", length = 5) +public interface AwsExceptions { + + AwsExceptions ex = Messages.getBundle(AwsExceptions.class); + + @Message(id = 18012, value = "Unable to select the Deserializer named `%s` for channel `%s` - too many matches (%d)") + AmbiguousResolutionException unableToFindDeserializer(String name, String channel, int count); + + @Message(id = 18014, value = "Unable to select the Serializer named `%s` for channel `%s` - too many matches (%d)") + AmbiguousResolutionException unableToFindSerializer(String name, String channel, int count); +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java new file mode 100644 index 0000000000..71911de552 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java @@ -0,0 +1,17 @@ +package io.smallrye.reactive.messaging.aws.serialization; + +/** + * This interface describes how a {@link String} is deserialized to an object + */ +public interface Deserializer { + + /** + * Deserialize a message to an object. + * + * @param payload message to deserialize + * @return the deserialized message + * // TODO: ex + * @throws RuntimeException in case the deserialization fails + */ + Object deserialize(String payload); +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java new file mode 100644 index 0000000000..eaf41c7ac8 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java @@ -0,0 +1,73 @@ +package io.smallrye.reactive.messaging.aws.serialization; + +import static io.smallrye.reactive.messaging.aws.i18n.AwsExceptions.ex; + +import java.util.Map; +import java.util.function.Function; + +import jakarta.enterprise.inject.AmbiguousResolutionException; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.literal.NamedLiteral; + +import io.smallrye.common.annotation.Identifier; +import io.smallrye.reactive.messaging.json.JsonMapping; +import io.smallrye.reactive.messaging.providers.i18n.ProviderLogging; + +public class SerializationResolver { + + public static Serializer resolveSerializer(Instance messageSerializer, + String name, String channel, + JsonMapping jsonMapping) { + return resolve(messageSerializer, name, defaultSerializer(jsonMapping), + count -> ex.unableToFindSerializer(name, channel, count)); + } + + private static Serializer defaultSerializer(JsonMapping jsonMapping) { + return payload -> { + if (jsonMapping != null) { + return jsonMapping.toJson(payload); + } else { + return String.valueOf(payload); + } + }; + } + + public static Deserializer resolveDeserializer(Instance messageDeserializer, + String name, String channel, + JsonMapping jsonMapping) { + return resolve(messageDeserializer, name, defaultDeserializer(jsonMapping), + count -> ex.unableToFindDeserializer(name, channel, count)); + } + + private static Deserializer defaultDeserializer(JsonMapping jsonMapping) { + return payload -> { + if (jsonMapping != null) { + return jsonMapping.fromJson(payload, Map.class); + } else { + return String.valueOf(payload); + } + }; + } + + private static T resolve(Instance instances, String name, + T defaultValue, + Function ambiguous) { + Instance instance = instances.select(Identifier.Literal.of(name)); + + if (instance.isUnsatisfied()) { + // this `if` block should be removed when support for the `@Named` annotation is removed + instance = instances.select(NamedLiteral.of(name)); + if (!instance.isUnsatisfied()) { + ProviderLogging.log.deprecatedNamed(); + } + } + + if (instance.isUnsatisfied()) { + return defaultValue; + } else if (instance.stream().count() > 1) { + throw ambiguous.apply((int) instance.stream().count()); + } else { + return instance.get(); + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java new file mode 100644 index 0000000000..689ceb6421 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java @@ -0,0 +1,17 @@ +package io.smallrye.reactive.messaging.aws.serialization; + +/** + * This interface describes how an object is serialized to a {@link String} + */ +public interface Serializer { + + /** + * Serialize an object to a {@link String} + * + * @param obj object to serialize + * @return serialized message + * // TODO: ex + * @throws RuntimeException in case serialization fails. + */ + String serialize(Object obj); +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml index 567c0b08d1..22d4ac77d8 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml @@ -14,10 +14,41 @@ SmallRye Reactive Messaging : Connector :: AWS SQS + + ${project.groupId} + smallrye-reactive-messaging-aws-core + ${project.version} + software.amazon.awssdk sqs + + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + url-connection-client + + + software.amazon.awssdk + apache-client + + + + + software.amazon.awssdk + netty-nio-client + true + + software.amazon.awssdk + aws-crt-client + true + + io.smallrye.reactive smallrye-reactive-messaging-otel diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java new file mode 100644 index 0000000000..e4ccfca377 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java @@ -0,0 +1,18 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +/** + * @author Christopher Holomek + * @since 01.10.2023 + */ +public abstract class SqsChannel { + + protected boolean closed = false; + + public boolean isClosed() { + return closed; + } + + public void close() { + this.closed = true; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index fd629a9c5f..676341c406 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -1,17 +1,22 @@ package io.smallrye.reactive.messaging.aws.sqs; import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; +import io.smallrye.reactive.messaging.aws.serialization.Serializer; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.connector.InboundConnector; import io.smallrye.reactive.messaging.connector.OutboundConnector; import io.smallrye.reactive.messaging.health.HealthReporter; import io.smallrye.reactive.messaging.json.JsonMapping; +import io.smallrye.reactive.messaging.providers.connectors.ExecutionHolder; +import io.vertx.mutiny.core.Vertx; import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.BeforeDestroyed; import jakarta.enterprise.event.Observes; import jakarta.enterprise.event.Reception; +import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import org.eclipse.microprofile.config.Config; @@ -28,6 +33,8 @@ import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; +import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveDeserializer; +import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveSerializer; import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; @@ -38,12 +45,15 @@ @ConnectorAttribute(name = "queue", type = "string", direction = INCOMING_AND_OUTGOING, description = "Set the SQS queue. If not set, the channel name is used") @ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") @ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") + @ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") @ConnectorAttribute(name = "create-queue.dlq.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") @ConnectorAttribute(name = "create-queue.dlq.prefix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") @ConnectorAttribute(name = "create-queue.dlq.suffix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") @ConnectorAttribute(name = "create-queue.dlq.max-receive-count", type = "int", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") +@ConnectorAttribute(name = "serialization-identifier", type = "string", direction = INCOMING_AND_OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") + // outgoing @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") @@ -54,18 +64,34 @@ public class SqsConnector implements InboundConnector, OutboundConnector, Health private final Map clients = new ConcurrentHashMap<>(); private final Map clientsByChannel = new ConcurrentHashMap<>(); - private final List outgoingChannels = new CopyOnWriteArrayList<>(); - // private final List> incomingChannels = new CopyOnWriteArrayList<>(); + private final List channels = new CopyOnWriteArrayList<>(); + + @Inject + private ExecutionHolder executionHolder; @Inject Instance jsonMapper; private JsonMapping jsonMapping; + @Inject + @Any + Instance messageSerializer; + + @Inject + @Any + Instance messageDeserializer; + + private Vertx vertx; + private SqsTargetResolver targetResolver; + @PostConstruct public void init() { + this.vertx = executionHolder.vertx(); + this.targetResolver = new SqsTargetResolver(); + if (jsonMapper.isUnsatisfied()) { - log.warn( - "Please add one of the additional mapping modules (-jsonb or -jackson) to be able to (de)serialize JSON messages."); + log.debug( + "No mapping modules (-jsonb or -jackson) defined. Fallback to toString() and String."); } else if (jsonMapper.isAmbiguous()) { log.warn( "Please select only one of the additional mapping modules (-jsonb or -jackson) to be able to (de)serialize JSON messages."); @@ -80,23 +106,36 @@ public void init() { public Flow.Publisher> getPublisher(Config config) { SqsConnectorIncomingConfiguration ic = new SqsConnectorIncomingConfiguration(config); - SqsAsyncClient client = clients.computeIfAbsent("", ignored -> createSqsClient(ic)); + SqsAsyncClient client = clients.computeIfAbsent(ic.getChannel(), ignored -> createSqsClient(ic, vertx)); clientsByChannel.put(ic.getChannel(), client); - return null; + final Serializer serializer = resolveSerializer(messageSerializer, + ic.getSerializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); + + try { + SqsIncomingChannel channel = new SqsIncomingChannel( + new SqsClientHolder<>(client, vertx, ic, targetResolver, serializer, null)); + channels.add(channel); + return channel.getPublisher(); + } catch (SqsException e) { + throw ex.illegalStateUnableToBuildProducer(e); + } } @Override public Flow.Subscriber> getSubscriber(Config config) { SqsConnectorOutgoingConfiguration oc = new SqsConnectorOutgoingConfiguration(config); - SqsAsyncClient client = clients.computeIfAbsent("", ignored -> createSqsClient(oc)); + SqsAsyncClient client = clients.computeIfAbsent(oc.getChannel(), ignored -> createSqsClient(oc, vertx)); clientsByChannel.put(oc.getChannel(), client); + final Deserializer deserializer = resolveDeserializer(messageDeserializer, oc + .getSerializationIdentifier().orElse(oc.getChannel()), oc.getChannel(), jsonMapping); + try { SqsOutgoingChannel channel = new SqsOutgoingChannel( - new SqsClientHolder<>(client, oc, jsonMapping, new SqsTargetResolver())); - outgoingChannels.add(channel); + new SqsClientHolder<>(client, vertx, oc, targetResolver, null, deserializer)); + channels.add(channel); return channel.getSubscriber(); } catch (SqsException e) { throw ex.illegalStateUnableToBuildConsumer(e); @@ -105,8 +144,7 @@ public Flow.Subscriber> getSubscriber(Config config) { public void terminate( @Observes(notifyObserver = Reception.IF_EXISTS) @Priority(50) @BeforeDestroyed(ApplicationScoped.class) Object event) { - // incomingChannels.forEach(PulsarIncomingChannel::close); - outgoingChannels.forEach(SqsOutgoingChannel::close); + channels.forEach(SqsChannel::close); for (SqsAsyncClient client : clients.values()) { try { client.close(); @@ -114,8 +152,7 @@ public void terminate( log.unableToCloseClient(e); } } - // incomingChannels.clear(); - outgoingChannels.clear(); + channels.clear(); clients.clear(); clientsByChannel.clear(); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java new file mode 100644 index 0000000000..2a03d17109 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -0,0 +1,50 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; + +import java.util.concurrent.Flow; + +import org.eclipse.microprofile.reactive.messaging.Message; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.providers.locals.ContextOperator; +import io.vertx.core.impl.EventLoopContext; +import io.vertx.core.impl.VertxInternal; + +/** + * @author Christopher Holomek + * @since 01.10.2023 + */ +public class SqsIncomingChannel extends SqsChannel { + + private final EventLoopContext context; + + private final Flow.Publisher> publisher; + + public SqsIncomingChannel(SqsClientHolder clientHolder) { + this.context = ((VertxInternal) clientHolder.getVertx().getDelegate()).createEventLoopContext(); + + this.publisher = Multi.createBy().repeating() + .uni(() -> (Uni>) receiveMessages(clientHolder, null)) + .until(ignore -> isClosed()) + .onItem().invoke(a -> { + }) + + // TODO: I think we do not need it in case the AWS SDK client is running on the netty eventloop thread. + // .emitOn(command -> context.runOnContext(event -> command.run())) + + // TODO: I think we do not need it in case the AWS SDK client is running on the netty eventloop thread. + // .emitOn(context.nettyEventLoop()) + .plug(ContextOperator::apply); + } + + public void close() { + + } + + public Flow.Publisher> getPublisher() { + return publisher; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index 4c43683658..f84fdaee31 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -1,5 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import org.eclipse.microprofile.reactive.messaging.Message; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.tuples.Tuple2; @@ -11,15 +19,8 @@ import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; import io.smallrye.reactive.messaging.tracing.TracingUtils; -import org.eclipse.microprofile.reactive.messaging.Message; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Flow; - -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; -public class SqsOutgoingChannel { +public class SqsOutgoingChannel extends SqsChannel { private final SqsClientHolder clientHolder; private final Flow.Subscriber> subscriber; @@ -95,8 +96,8 @@ private void tracing(SqsOutgoingMessage message) { // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. // .withMessageId("") .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; + // We do not set payload size. This would require to calculate it, which is less performant. + ; TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java deleted file mode 100644 index f9cda2a2d1..0000000000 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsSubscribe.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.smallrye.reactive.messaging.aws.sqs; - -/** - * @author Christopher Holomek - * @since 01.10.2023 - */ -public class SqsSubscribe { -} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java index 76cf62514e..4e5a0424dd 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java @@ -1,5 +1,8 @@ package io.smallrye.reactive.messaging.aws.sqs; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.action.CreateQueueAction; import io.smallrye.reactive.messaging.aws.sqs.action.GetQueueUrlAction; @@ -8,9 +11,6 @@ import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - public class SqsTargetResolver { private final Map> CACHE = new ConcurrentHashMap<>(); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java index cff072f6b0..703a0f3f11 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -1,21 +1,24 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import java.util.HashMap; +import java.util.Map; + import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsCreateQueueMetadata; import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; +import io.vertx.core.json.JsonObject; import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; import software.amazon.awssdk.services.sqs.model.CreateQueueResponse; import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import java.util.HashMap; -import java.util.Map; - /** - * AWS Documentation + * AWS + * Documentation */ public class CreateQueueAction { @@ -35,8 +38,7 @@ public static Uni createQueue( SqsCreateQueueMetadata createQueueDlqMetadata = metadata.getCreateQueueDlqMetadata(); uni = uni.onItem().transformToUni(ignore -> createDlq( clientHolder, queueName, config, - createQueueDlqMetadata.getAttributes(), createQueueDlqMetadata.getTags() - )); + createQueueDlqMetadata.getAttributes(), createQueueDlqMetadata.getTags())); } return uni.onItem().transformToUni(preparedAttributes -> { @@ -51,9 +53,9 @@ public static Uni createQueue( } private static Uni> createDlq(SqsClientHolder clientHolder, String queueName, - SqsConnectorCommonConfiguration config, - Map attributes, - Map tags) { + SqsConnectorCommonConfiguration config, + Map attributes, + Map tags) { Uni uni = createQueue(clientHolder, config.getCreateQueueDlqPrefix() + queueName + config.getCreateQueueDlqSuffix(), @@ -61,20 +63,20 @@ private static Uni> createDlq(SqsClientHolder return uni.onItem().transformToUni(response -> getQueueArn(clientHolder, response)) .onItem().transform(arn -> Map.of( - QueueAttributeName.REDRIVE_POLICY, createRedrivePolicy(clientHolder, config, arn))); + QueueAttributeName.REDRIVE_POLICY, createRedrivePolicy(config, arn))); } - private static String createRedrivePolicy(SqsClientHolder clientHolder, SqsConnectorCommonConfiguration config, - String arn) { - return clientHolder.getJsonMapping().toJson(Map.of( - "deadLetterTargetArn", arn, - "maxReceiveCount", config.getCreateQueueDlqMaxReceiveCount() - )); + private static String createRedrivePolicy(SqsConnectorCommonConfiguration config, + String arn) { + + final JsonObject data = JsonObject.of("deadLetterTargetArn", arn) + .put("maxReceiveCount", config.getCreateQueueDlqMaxReceiveCount()); + return data.toString(); } private static Uni createQueue(SqsClientHolder clientHolder, String queueName, - Map attributes, - Map tags) { + Map attributes, + Map tags) { CreateQueueRequest request = CreateQueueRequest.builder() .queueName(queueName) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java index 887f061137..516f89c641 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java @@ -19,10 +19,10 @@ public static Uni resolveQueueUrl( SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); return Uni.createFrom().completionStage( - clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() - .queueName(config.getQueue().orElse(config.getChannel())) - .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) - .build())) + clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() + .queueName(config.getQueue().orElse(config.getChannel())) + .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) + .build())) .onItem().transform(GetQueueUrlResponse::queueUrl); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java index b93db3c2e7..cff637b02f 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java @@ -1,8 +1,27 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import org.eclipse.microprofile.reactive.messaging.Message; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; + /** - * @author Christopher Holomek - * @since 01.10.2023 + * AWS Documentation */ public class ReceiveMessageAction { + + public static > Uni receiveMessages( + SqsClientHolder clientHolder, + SqsTarget target) { + + final ReceiveMessageRequest request = ReceiveMessageRequest.builder() + .queueUrl(target.getTargetUrl()) + + .build(); + + return null; + } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java index 7043901606..39bc4eb30e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -1,15 +1,14 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import java.time.Duration; + import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; -import io.smallrye.reactive.messaging.aws.sqs.util.Helper; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; -import java.time.Duration; - /** * AWS Documentation */ @@ -17,7 +16,8 @@ public class SendMessageAction { public static Uni sendMessage( final SqsClientHolder clientHolder, final SqsOutgoingMessage message) { - String payload = Helper.serialize(message, clientHolder.getJsonMapping()); + + String payload = clientHolder.getSerializer().serialize(message); SendMessageRequest request = SendMessageRequest.builder() .queueUrl(message.getTarget().getTargetUrl()) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java index 25ca7ae620..cad72ce3c0 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java @@ -1,5 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.OutgoingMessageMetadata; @@ -7,21 +15,13 @@ import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; -import io.smallrye.reactive.messaging.aws.sqs.util.Helper; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; - /** - * AWS Documentation + * AWS + * Documentation */ public class SendMessageBatchAction { @@ -59,7 +59,7 @@ private static SendMessageBatchRequest createRequest( Map entryMap = new HashMap<>(); messages.forEach(msg -> { - String payload = Helper.serialize(msg, clientHolder.getJsonMapping()); + String payload = clientHolder.getSerializer().serialize(msg); String id = UUID.randomUUID().toString(); final SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java index 46c79e3260..3d1c965030 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java @@ -1,16 +1,61 @@ package io.smallrye.reactive.messaging.aws.sqs.client; -import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; -import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; +import io.vertx.core.impl.EventLoopContext; +import io.vertx.core.impl.VertxInternal; +import io.vertx.mutiny.core.Vertx; +import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; import software.amazon.awssdk.services.sqs.SqsAsyncClient; +import software.amazon.awssdk.services.sqs.SqsAsyncClientBuilder; +/** + * Based on some logic of the quarkiverse lib for aws + * TODO: Not sure about all of this. Maybe just for qualified SqsAsyncClient beans? This would allow to use it with + * Quarkiverse extension. Maybe. Or manually created clients. Otherwise, I would be forced to forward all possible + * configurations. But sometimes configs are not flexible enough. The credentials provider topic is already + * providing a lot of different approaches to configure them. + * But using something like Quarkiverse would also require that we check if netty eventloop is used or not. + */ public class SqsClientFactory { - public static SqsAsyncClient createSqsClient(final SqsConnectorIncomingConfiguration config) { - return null; + public static final String NETTY_HTTP_SERVICE = "software.amazon.awssdk.http.nio.netty.NettySdkAsyncHttpService"; + + // TODO: I think this makes no sense when we already use netty in vertx. + public static final String AWS_CRT_HTTP_SERVICE = "software.amazon.awssdk.http.crt.AwsCrtSdkHttpService"; + + public static SqsAsyncClient createSqsClient(SqsConnectorCommonConfiguration config, Vertx vertx) { + + final SqsAsyncClientBuilder builder = SqsAsyncClient.builder(); + + final EventLoopContext context = ((VertxInternal) vertx.getDelegate()).createEventLoopContext(); + + if (nettyExists()) { + builder.asyncConfiguration(b -> b + .advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, context.nettyEventLoop())) + .httpClientBuilder(NettyNioAsyncHttpClient.builder() + .eventLoopGroup(SdkEventLoopGroup.create(context.nettyEventLoop()))); + } + + return builder.build(); + } + + public static boolean nettyExists() { + try { + Class.forName(NETTY_HTTP_SERVICE); + return true; + } catch (Exception e) { + return false; + } } - public static SqsAsyncClient createSqsClient(final SqsConnectorOutgoingConfiguration config) { - return null; + public static boolean awsCrtExists() { + try { + Class.forName(AWS_CRT_HTTP_SERVICE); + return true; + } catch (Exception e) { + return false; + } } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java index de77269239..b5d597bace 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -1,35 +1,22 @@ package io.smallrye.reactive.messaging.aws.sqs.client; +import io.smallrye.reactive.messaging.aws.client.ClientHolder; +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; +import io.smallrye.reactive.messaging.aws.serialization.Serializer; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; import io.smallrye.reactive.messaging.aws.sqs.SqsTargetResolver; -import io.smallrye.reactive.messaging.json.JsonMapping; +import io.vertx.mutiny.core.Vertx; import software.amazon.awssdk.services.sqs.SqsAsyncClient; -public class SqsClientHolder { - private final SqsAsyncClient client; - private final C config; - private final JsonMapping jsonMapping; +public class SqsClientHolder extends ClientHolder { private final SqsTargetResolver targetResolver; - public SqsClientHolder(SqsAsyncClient client, C config, JsonMapping jsonMapping, SqsTargetResolver targetResolver) { - this.client = client; - this.config = config; - this.jsonMapping = jsonMapping; + public SqsClientHolder(SqsAsyncClient client, Vertx vertx, C config, SqsTargetResolver targetResolver, + Serializer serializer, Deserializer deserializer) { + super(client, vertx, config, serializer, deserializer); this.targetResolver = targetResolver; } - public SqsAsyncClient getClient() { - return client; - } - - public C getConfig() { - return config; - } - - public JsonMapping getJsonMapping() { - return jsonMapping; - } - public SqsTargetResolver getTargetCache() { return targetResolver; } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java deleted file mode 100644 index a14ee078e6..0000000000 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/config/ConfigResolver.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.smallrye.reactive.messaging.aws.sqs.config; - -public class ConfigResolver { -} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java index 2d09f1a7cf..a3e629a4aa 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java @@ -1,10 +1,10 @@ package io.smallrye.reactive.messaging.aws.sqs.message; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; - import java.util.HashMap; import java.util.Map; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; + public class SqsCreateQueueMetadata { private Map attributes = new HashMap<>(); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java deleted file mode 100644 index cc629d3d66..0000000000 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/util/Helper.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.smallrye.reactive.messaging.aws.sqs.util; - -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; -import io.smallrye.reactive.messaging.json.JsonMapping; - -public class Helper { - - public static String serialize(SqsMessage message, JsonMapping jsonMapping) { - if (message.getPayload() instanceof String) { - return (String) message.getPayload(); - } else { - return jsonMapping.toJson(message.getPayload()); - } - } -} From a545866aa53385c5d2e93bf0375cd53338eaa420 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Sun, 1 Oct 2023 18:58:44 +0200 Subject: [PATCH 07/14] Cleanup configuration so that it is considered that incoming channel cannot handle some things. The reason is that the metadata is not available but instead is created by this impl and more for providing information from lib to user. So I moved it to configuration. This is not really nice, because I need to configure 4 maps. The config impl. here cannot handle that, so manual parsing is necessary. This is strange. Maybe this needs to change. Receive message implemented sort of. Deletion / Confirmation is missing. Should be the same as Sending. Also with batching. --- .../messaging/aws/config/ConfigHelper.java | 37 ++++++++++ .../messaging/aws/sqs/SqsChannel.java | 4 -- .../messaging/aws/sqs/SqsConnector.java | 72 +++++++++++-------- .../messaging/aws/sqs/SqsIncomingChannel.java | 54 ++++++++++---- .../messaging/aws/sqs/SqsOutgoingChannel.java | 25 ++++--- .../messaging/aws/sqs/SqsTargetResolver.java | 11 ++- .../aws/sqs/action/CreateQueueAction.java | 50 ++++++------- .../aws/sqs/action/GetQueueUrlAction.java | 8 +-- .../aws/sqs/action/ReceiveMessageAction.java | 21 ++++-- .../aws/sqs/client/SqsClientHolder.java | 2 +- .../sqs/message/SqsCreateQueueMetadata.java | 30 -------- .../aws/sqs/message/SqsIncomingMessage.java | 15 ++++ .../message/SqsIncomingMessageMetadata.java | 4 ++ .../messaging/aws/sqs/message/SqsMessage.java | 26 +++++-- .../aws/sqs/message/SqsMessageMetadata.java | 41 ----------- .../aws/sqs/message/SqsOutgoingMessage.java | 9 +-- 16 files changed, 223 insertions(+), 186 deletions(-) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java delete mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java new file mode 100644 index 0000000000..6408b95d20 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java @@ -0,0 +1,37 @@ +package io.smallrye.reactive.messaging.aws.config; + +import java.util.*; + +/** + * @author Christopher Holomek + * @since 01.10.2023 + */ +public class ConfigHelper { + + public static List parseToList(String valueToParse) { + List result = new ArrayList<>(); + + Arrays.stream(valueToParse.split(",")) + .map(String::trim) + .forEach(result::add); + + return result; + } + + public static Map parseToMap(String valueToParse) { + Map result = new HashMap<>(); + + Arrays.stream(valueToParse.split(",")) + .map(String::trim) + .forEach(keyValue -> { + String[] keyValueSplit = keyValue.split(":"); + if (keyValueSplit.length == 2) { + String key = keyValueSplit[0]; + String value = keyValueSplit[1]; + result.put(key, value); + } + }); + + return result; + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java index e4ccfca377..f39e33f4e8 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsChannel.java @@ -1,9 +1,5 @@ package io.smallrye.reactive.messaging.aws.sqs; -/** - * @author Christopher Holomek - * @since 01.10.2023 - */ public abstract class SqsChannel { protected boolean closed = false; diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index 676341c406..ce84d2e362 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -1,15 +1,19 @@ package io.smallrye.reactive.messaging.aws.sqs; -import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; -import io.smallrye.reactive.messaging.aws.serialization.Deserializer; -import io.smallrye.reactive.messaging.aws.serialization.Serializer; -import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.connector.InboundConnector; -import io.smallrye.reactive.messaging.connector.OutboundConnector; -import io.smallrye.reactive.messaging.health.HealthReporter; -import io.smallrye.reactive.messaging.json.JsonMapping; -import io.smallrye.reactive.messaging.providers.connectors.ExecutionHolder; -import io.vertx.mutiny.core.Vertx; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; +import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveDeserializer; +import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveSerializer; +import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Flow; + import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; @@ -19,26 +23,24 @@ import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; + import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.reactive.messaging.Message; import org.eclipse.microprofile.reactive.messaging.spi.Connector; + +import io.smallrye.reactive.messaging.annotations.ConnectorAttribute; +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; +import io.smallrye.reactive.messaging.aws.serialization.Serializer; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.connector.InboundConnector; +import io.smallrye.reactive.messaging.connector.OutboundConnector; +import io.smallrye.reactive.messaging.health.HealthReporter; +import io.smallrye.reactive.messaging.json.JsonMapping; +import io.smallrye.reactive.messaging.providers.connectors.ExecutionHolder; +import io.vertx.mutiny.core.Vertx; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.SqsException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Flow; - -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; -import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; -import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveDeserializer; -import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveSerializer; -import static io.smallrye.reactive.messaging.aws.sqs.client.SqsClientFactory.createSqsClient; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsExceptions.ex; -import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; - @ApplicationScoped @Connector(SqsConnector.CONNECTOR_NAME) // common @@ -46,11 +48,19 @@ @ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") @ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "queue-resolver.queue-owner-aws-account-id", type = "string", direction = INCOMING_AND_OUTGOING, description = "During queue url resolving it is possible to overwrite the queue owner.") + @ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") -@ConnectorAttribute(name = "create-queue.dlq.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") -@ConnectorAttribute(name = "create-queue.dlq.prefix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") -@ConnectorAttribute(name = "create-queue.dlq.suffix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") -@ConnectorAttribute(name = "create-queue.dlq.max-receive-count", type = "int", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") +// TODO: Not sure how to load maps. Maybe: key:value,key:value. It is not very efficient, but it is cached by the SqsTargetResolver. So maybe it does not matter. +// Otherwise, I would need to wrap the config so that I can keep using the easy generated way, but also overwrite methods, to add config internal caching. +@ConnectorAttribute(name = "create-queue.attributes", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of attributes for queue creation. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "create-queue.tags", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of tags for queue creation. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.prefix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.suffix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.max-receive-count", type = "int", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.attributes", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of attributes for queue creation. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.tags", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of tags for queue creation. Default empty.", defaultValue = "") @ConnectorAttribute(name = "serialization-identifier", type = "string", direction = INCOMING_AND_OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") @@ -58,6 +68,12 @@ @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") // incomming +@ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") +@ConnectorAttribute(name = "wait-time-seconds", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) for which the call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than WaitTimeSeconds. If no messages are available and the wait time expires, the call returns successfully with an empty list of messages. Default 20s.", defaultValue = "20") +@ConnectorAttribute(name = "visibility-timeout", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) that the received messages are hidden from subsequent retrieve requests after being retrieved by a request. Default 15s.", defaultValue = "15") +@ConnectorAttribute(name = "attribute-names", type = "string", direction = ConnectorAttribute.Direction.INCOMING, description = "A comma separated list of attributes that need to be returned along with each message. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "message-attribute-names", type = "string", direction = ConnectorAttribute.Direction.INCOMING, description = "A comma separated list of message attributes that need to be returned along with each message. Default empty.", defaultValue = "") + public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { static final String CONNECTOR_NAME = "smallrye-aws-sqs"; diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java index 2a03d17109..7b22adc419 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -1,24 +1,25 @@ package io.smallrye.reactive.messaging.aws.sqs; -import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; - -import java.util.concurrent.Flow; - -import org.eclipse.microprofile.reactive.messaging.Message; - import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; import io.smallrye.reactive.messaging.providers.locals.ContextOperator; import io.vertx.core.impl.EventLoopContext; import io.vertx.core.impl.VertxInternal; +import org.eclipse.microprofile.reactive.messaging.Message; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; + +import java.util.List; +import java.util.concurrent.Flow; +import java.util.function.Supplier; + +import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToList; +import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; -/** - * @author Christopher Holomek - * @since 01.10.2023 - */ public class SqsIncomingChannel extends SqsChannel { + // TODO: remove if not needed. private final EventLoopContext context; private final Flow.Publisher> publisher; @@ -26,11 +27,15 @@ public class SqsIncomingChannel extends SqsChannel { public SqsIncomingChannel(SqsClientHolder clientHolder) { this.context = ((VertxInternal) clientHolder.getVertx().getDelegate()).createEventLoopContext(); + // For performance reasons created outside the stream. + final Supplier> receiveMessagesSupplier = receiveMessagesSupplier(clientHolder); + this.publisher = Multi.createBy().repeating() - .uni(() -> (Uni>) receiveMessages(clientHolder, null)) + .uni(receiveMessagesSupplier::get) .until(ignore -> isClosed()) - .onItem().invoke(a -> { - }) + .skip().where(response -> !response.hasMessages()) + .onItem().transformToMulti(SqsIncomingChannel::createMultiOfMessages).merge() + .onItem().transformToUniAndConcatenate(this::initCallbacks) // TODO: I think we do not need it in case the AWS SDK client is running on the netty eventloop thread. // .emitOn(command -> context.runOnContext(event -> command.run())) @@ -40,8 +45,29 @@ public SqsIncomingChannel(SqsClientHolder cli .plug(ContextOperator::apply); } - public void close() { + private static Supplier> receiveMessagesSupplier( + SqsClientHolder clientHolder + ) { + // TODO: is there an easier way to get list and map configuration? Otherwise, it is important to make + // sure that the data is not parsed too often. :( + final List attributeNames = parseToList(clientHolder.getConfig().getAttributeNames()); + final List messageAttributeNames = parseToList(clientHolder.getConfig().getMessageAttributeNames()); + + return () -> clientHolder.getTargetResolver().resolveTarget(clientHolder) + .onItem().transformToUni(target -> receiveMessages(clientHolder, target, attributeNames, messageAttributeNames)); + } + private static Multi createMultiOfMessages(ReceiveMessageResponse response) { + return Multi.createFrom().iterable(response.messages()) + .onItem().transform(SqsIncomingMessage::from); + } + + private Uni initCallbacks(SqsIncomingMessage msg) { + return Uni.createFrom().item(msg); + } + + public void close() { + // TODO: close } public Flow.Publisher> getPublisher() { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index f84fdaee31..baea963e54 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -1,13 +1,5 @@ package io.smallrye.reactive.messaging.aws.sqs; -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Flow; - -import org.eclipse.microprofile.reactive.messaging.Message; - import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.tuples.Tuple2; @@ -19,6 +11,13 @@ import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; import io.smallrye.reactive.messaging.tracing.TracingUtils; +import org.eclipse.microprofile.reactive.messaging.Message; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; public class SqsOutgoingChannel extends SqsChannel { @@ -61,7 +60,7 @@ private Multi createStreamWithBatching(Multi> m) { } private Uni> addTargetInformation(SqsOutgoingMessage msg) { - return clientHolder.getTargetCache().getTarget(clientHolder, msg) + return clientHolder.getTargetResolver().resolveTarget(clientHolder) .onItem().transform(target -> { msg.withTarget(target); return msg; @@ -70,7 +69,7 @@ private Uni> addTargetInformation(SqsOutgoingMes private Uni sendMessage(SqsOutgoingMessage message) { final SqsOutgoingMessage sqsMessage = SqsOutgoingMessage.from(message); - return clientHolder.getTargetCache().getTarget(clientHolder, sqsMessage) + return clientHolder.getTargetResolver().resolveTarget(clientHolder) .onItem().transformToUni(target -> SendMessageAction.sendMessage(clientHolder, sqsMessage)); } @@ -96,8 +95,8 @@ private void tracing(SqsOutgoingMessage message) { // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. // .withMessageId("") .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; + // We do not set payload size. This would require to calculate it, which is less performant. + ; TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); } } @@ -107,6 +106,6 @@ public Flow.Subscriber> getSubscriber() { } public void close() { - + // TODO: close } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java index 4e5a0424dd..89a019e2e3 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsTargetResolver.java @@ -7,24 +7,21 @@ import io.smallrye.reactive.messaging.aws.sqs.action.CreateQueueAction; import io.smallrye.reactive.messaging.aws.sqs.action.GetQueueUrlAction; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; public class SqsTargetResolver { private final Map> CACHE = new ConcurrentHashMap<>(); - public Uni getTarget( - final SqsClientHolder clientHolder, final SqsMessage message) { + public Uni resolveTarget(final SqsClientHolder clientHolder) { final SqsConnectorCommonConfiguration config = clientHolder.getConfig(); return CACHE.computeIfAbsent( clientHolder.getConfig().getQueue().orElse(config.getChannel()), - key -> GetQueueUrlAction.resolveQueueUrl(clientHolder, message) + queueName -> GetQueueUrlAction.resolveQueueUrl(clientHolder) .onFailure(QueueDoesNotExistException.class) - .call(() -> CreateQueueAction.createQueue(clientHolder, message)) - .onItem().transform(url -> new SqsTarget(key, url)) + .call(() -> CreateQueueAction.createQueue(clientHolder, queueName)) + .onItem().transform(url -> new SqsTarget(queueName, url)) .memoize().indefinitely()); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java index 703a0f3f11..36d5406e3d 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -1,14 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToMap; + import java.util.HashMap; import java.util.Map; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsCreateQueueMetadata; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; import io.vertx.core.json.JsonObject; import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; import software.amazon.awssdk.services.sqs.model.CreateQueueResponse; @@ -22,65 +21,68 @@ */ public class CreateQueueAction { - public static Uni createQueue( - SqsClientHolder clientHolder, SqsMessage message) { + public static Uni createQueue(SqsClientHolder clientHolder, String queueName) { SqsConnectorCommonConfiguration config = clientHolder.getConfig(); if (!config.getCreateQueueEnabled()) { return Uni.createFrom().nullItem(); } - String queueName = message.getTarget().getTargetName(); - Uni> uni = Uni.createFrom().item(Map.of()); - M metadata = message.getSqsMetadata(); + Uni> uni = Uni.createFrom().item(Map.of()); + + if (config.getCreateQueueDeadLetterQueueEnabled()) { + // Parsing is slow. But we assume that creating queues does not happen that often. + final Map attributes = parseToMap(config.getCreateQueueDeadLetterQueueAttributes()); + final Map tags = parseToMap(config.getCreateQueueDeadLetterQueueTags()); - if (config.getCreateQueueDlqEnabled()) { - SqsCreateQueueMetadata createQueueDlqMetadata = metadata.getCreateQueueDlqMetadata(); uni = uni.onItem().transformToUni(ignore -> createDlq( clientHolder, queueName, config, - createQueueDlqMetadata.getAttributes(), createQueueDlqMetadata.getTags())); + attributes, tags)); } return uni.onItem().transformToUni(preparedAttributes -> { - SqsCreateQueueMetadata createQueueMetadata = metadata.getCreateQueueMetadata(); + // Parsing is slow. But we assume that creating queues does not happen that often. + final Map configAttributes = parseToMap(config.getCreateQueueAttributes()); + final Map tags = parseToMap(config.getCreateQueueTags()); + final Map attributes = new HashMap<>(preparedAttributes.size() + configAttributes.size()); - HashMap attributes = new HashMap<>(); attributes.putAll(preparedAttributes); - attributes.putAll(createQueueMetadata.getAttributes()); + attributes.putAll(configAttributes); - return createQueue(clientHolder, queueName, attributes, createQueueMetadata.getTags()); + return createQueue(clientHolder, queueName, attributes, tags); }).onItem().transform(CreateQueueResponse::queueUrl); } - private static Uni> createDlq(SqsClientHolder clientHolder, String queueName, + private static Uni> createDlq( + SqsClientHolder clientHolder, String queueName, SqsConnectorCommonConfiguration config, - Map attributes, + Map attributes, Map tags) { Uni uni = createQueue(clientHolder, - config.getCreateQueueDlqPrefix() + queueName + config.getCreateQueueDlqSuffix(), + config.getCreateQueueDeadLetterQueuePrefix() + queueName + config.getCreateQueueDeadLetterQueueSuffix(), attributes, tags); return uni.onItem().transformToUni(response -> getQueueArn(clientHolder, response)) .onItem().transform(arn -> Map.of( - QueueAttributeName.REDRIVE_POLICY, createRedrivePolicy(config, arn))); + QueueAttributeName.REDRIVE_POLICY.toString(), createRedrivePolicy(config, arn))); } private static String createRedrivePolicy(SqsConnectorCommonConfiguration config, String arn) { final JsonObject data = JsonObject.of("deadLetterTargetArn", arn) - .put("maxReceiveCount", config.getCreateQueueDlqMaxReceiveCount()); + .put("maxReceiveCount", config.getCreateQueueDeadLetterQueueMaxReceiveCount()); return data.toString(); } - private static Uni createQueue(SqsClientHolder clientHolder, String queueName, - Map attributes, + private static Uni createQueue( + SqsClientHolder clientHolder, String queueName, + Map attributes, Map tags) { - CreateQueueRequest request = CreateQueueRequest.builder() .queueName(queueName) - .attributes(attributes) + .attributesWithStrings(attributes) .tags(tags) .build(); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java index 516f89c641..2aa35f2e90 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/GetQueueUrlAction.java @@ -3,8 +3,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessage; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsMessageMetadata; import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; @@ -13,15 +11,13 @@ */ public class GetQueueUrlAction { - public static Uni resolveQueueUrl( - SqsClientHolder clientHolder, SqsMessage message) { + public static Uni resolveQueueUrl(SqsClientHolder clientHolder) { SqsConnectorCommonConfiguration config = clientHolder.getConfig(); - SqsMessageMetadata sqsMetadata = message.getSqsMetadata(); return Uni.createFrom().completionStage( clientHolder.getClient().getQueueUrl(GetQueueUrlRequest.builder() .queueName(config.getQueue().orElse(config.getChannel())) - .queueOwnerAWSAccountId(sqsMetadata.getQueueOwnerAWSAccountId()) + .queueOwnerAWSAccountId(config.getQueueResolverQueueOwnerAwsAccountId().orElse(null)) .build())) .onItem().transform(GetQueueUrlResponse::queueUrl); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java index cff637b02f..a2392174b7 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java @@ -1,27 +1,36 @@ package io.smallrye.reactive.messaging.aws.sqs.action; -import org.eclipse.microprofile.reactive.messaging.Message; - import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; + +import java.util.List; /** * AWS Documentation */ public class ReceiveMessageAction { - public static > Uni receiveMessages( + public static Uni receiveMessages( SqsClientHolder clientHolder, - SqsTarget target) { + SqsTarget target, List attributeNames, List messageAttributeNames) { + + final SqsConnectorIncomingConfiguration config = clientHolder.getConfig(); + final ReceiveMessageRequest request = ReceiveMessageRequest.builder() .queueUrl(target.getTargetUrl()) - + .maxNumberOfMessages(config.getMaxNumberOfMessages()) + .waitTimeSeconds(config.getWaitTimeSeconds()) + .visibilityTimeout(config.getVisibilityTimeout()) + .attributeNamesWithStrings(attributeNames) + .messageAttributeNames(messageAttributeNames) .build(); - return null; + return Uni.createFrom().completionStage(clientHolder.getClient().receiveMessage(request)); } + } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java index b5d597bace..7689fa0fbe 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -17,7 +17,7 @@ public SqsClientHolder(SqsAsyncClient client, Vertx vertx, C config, SqsTargetRe this.targetResolver = targetResolver; } - public SqsTargetResolver getTargetCache() { + public SqsTargetResolver getTargetResolver() { return targetResolver; } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java deleted file mode 100644 index a3e629a4aa..0000000000 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsCreateQueueMetadata.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.smallrye.reactive.messaging.aws.sqs.message; - -import java.util.HashMap; -import java.util.Map; - -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; - -public class SqsCreateQueueMetadata { - - private Map attributes = new HashMap<>(); - private Map tags = new HashMap<>(); - - public Map getAttributes() { - return attributes; - } - - public SqsCreateQueueMetadata withAttributes(Map attributes) { - this.attributes = attributes; - return this; - } - - public Map getTags() { - return tags; - } - - public SqsCreateQueueMetadata withTags(Map tags) { - this.tags = tags; - return this; - } -} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java new file mode 100644 index 0000000000..2d7a137b55 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java @@ -0,0 +1,15 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +import software.amazon.awssdk.services.sqs.model.Message; + +public class SqsIncomingMessage extends SqsMessage { + + private SqsIncomingMessage(Message payload, SqsIncomingMessageMetadata metadata) { + super(payload, metadata); + } + + public static SqsIncomingMessage from(Message payload) { + final SqsIncomingMessageMetadata sqsMetaData = new SqsIncomingMessageMetadata(); + return new SqsIncomingMessage(payload, sqsMetaData); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java new file mode 100644 index 0000000000..5040f9122b --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java @@ -0,0 +1,4 @@ +package io.smallrye.reactive.messaging.aws.sqs.message; + +public class SqsIncomingMessageMetadata extends SqsMessageMetadata { +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java index 71902ea414..e97d811f58 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessage.java @@ -1,15 +1,33 @@ package io.smallrye.reactive.messaging.aws.sqs.message; +import static io.smallrye.reactive.messaging.providers.locals.ContextAwareMessage.captureContextMetadata; + +import org.eclipse.microprofile.reactive.messaging.Metadata; + import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.providers.locals.ContextAwareMessage; public abstract class SqsMessage implements ContextAwareMessage { + private final T payload; private SqsTarget target; - private final M metadata; + private final M sqsMetadata; + private final Metadata metadata; + + public SqsMessage(T payload, M sqsMetadata) { + this.payload = payload; + this.sqsMetadata = sqsMetadata; + this.metadata = captureContextMetadata(sqsMetadata); + } - public SqsMessage(M metadata) { - this.metadata = metadata; + @Override + public T getPayload() { + return payload; + } + + @Override + public Metadata getMetadata() { + return metadata; } public SqsTarget getTarget() { @@ -22,6 +40,6 @@ public SqsMessage withTarget(SqsTarget target) { } public M getSqsMetadata() { - return metadata; + return sqsMetadata; } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java index 256d36de4a..94868165df 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsMessageMetadata.java @@ -3,15 +3,7 @@ public abstract class SqsMessageMetadata { private String queue; - private String queueOwnerAWSAccountId; private String conversationId; - private SqsCreateQueueMetadata createQueueMetadata; - private SqsCreateQueueMetadata createQueueDlqMetadata; - - public SqsMessageMetadata() { - createQueueMetadata = new SqsCreateQueueMetadata(); - createQueueDlqMetadata = new SqsCreateQueueMetadata(); - } /** * Get the name of the queue @@ -27,21 +19,6 @@ public SqsMessageMetadata withQueue(String queue) { return this; } - /** - * During queue name resolving it is possible to overwrite the AWS account id. If not specified the - * AWS accounts id from the provided client credentials are used - * - * @return overwritten AWS account id - */ - public String getQueueOwnerAWSAccountId() { - return queueOwnerAWSAccountId; - } - - public SqsMessageMetadata withQueueOwnerAWSAccountId(String queueOwnerAWSAccountId) { - this.queueOwnerAWSAccountId = queueOwnerAWSAccountId; - return this; - } - public String getConversationId() { return conversationId; } @@ -50,22 +27,4 @@ public SqsMessageMetadata withConversationId(String conversationId) { this.conversationId = conversationId; return this; } - - public SqsCreateQueueMetadata getCreateQueueMetadata() { - return createQueueMetadata; - } - - public SqsMessageMetadata withCreateQueueMetadata(SqsCreateQueueMetadata createQueueMetadata) { - this.createQueueMetadata = createQueueMetadata; - return this; - } - - public SqsCreateQueueMetadata getCreateQueueDlqMetadata() { - return createQueueDlqMetadata; - } - - public SqsMessageMetadata withCreateQueueDlqMetadata(SqsCreateQueueMetadata createQueueDlqMetadata) { - this.createQueueDlqMetadata = createQueueDlqMetadata; - return this; - } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java index b8fe067888..0c0a78019d 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java @@ -8,23 +8,16 @@ public class SqsOutgoingMessage extends SqsMessage { - private final T payload; private final Supplier> ack; private final Function> nack; public SqsOutgoingMessage(final T payload, final SqsOutgoingMessageMetadata metadata, Supplier> ack, Function> nack) { - super(metadata); - this.payload = payload; + super(payload, metadata); this.ack = ack; this.nack = nack; } - @Override - public T getPayload() { - return payload; - } - @Override public Supplier> getAck() { return this.ack; From adc4bfd0e2aeb80481a118c4fdfff31eb51c1802 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:21:15 +0100 Subject: [PATCH 08/14] Formatting --- .../messaging/aws/sqs/SqsIncomingChannel.java | 23 ++++++++++--------- .../messaging/aws/sqs/SqsOutgoingChannel.java | 19 +++++++-------- .../aws/sqs/action/ReceiveMessageAction.java | 5 ++-- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java index 7b22adc419..7cd7b4ba62 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -1,5 +1,14 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToList; +import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; + +import java.util.List; +import java.util.concurrent.Flow; +import java.util.function.Supplier; + +import org.eclipse.microprofile.reactive.messaging.Message; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; @@ -7,16 +16,8 @@ import io.smallrye.reactive.messaging.providers.locals.ContextOperator; import io.vertx.core.impl.EventLoopContext; import io.vertx.core.impl.VertxInternal; -import org.eclipse.microprofile.reactive.messaging.Message; import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; -import java.util.List; -import java.util.concurrent.Flow; -import java.util.function.Supplier; - -import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToList; -import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; - public class SqsIncomingChannel extends SqsChannel { // TODO: remove if not needed. @@ -46,15 +47,15 @@ public SqsIncomingChannel(SqsClientHolder cli } private static Supplier> receiveMessagesSupplier( - SqsClientHolder clientHolder - ) { + SqsClientHolder clientHolder) { // TODO: is there an easier way to get list and map configuration? Otherwise, it is important to make // sure that the data is not parsed too often. :( final List attributeNames = parseToList(clientHolder.getConfig().getAttributeNames()); final List messageAttributeNames = parseToList(clientHolder.getConfig().getMessageAttributeNames()); return () -> clientHolder.getTargetResolver().resolveTarget(clientHolder) - .onItem().transformToUni(target -> receiveMessages(clientHolder, target, attributeNames, messageAttributeNames)); + .onItem() + .transformToUni(target -> receiveMessages(clientHolder, target, attributeNames, messageAttributeNames)); } private static Multi createMultiOfMessages(ReceiveMessageResponse response) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index baea963e54..0817097380 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -1,5 +1,13 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Flow; + +import org.eclipse.microprofile.reactive.messaging.Message; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.tuples.Tuple2; @@ -11,13 +19,6 @@ import io.smallrye.reactive.messaging.aws.sqs.tracing.SqsTrace; import io.smallrye.reactive.messaging.providers.helpers.MultiUtils; import io.smallrye.reactive.messaging.tracing.TracingUtils; -import org.eclipse.microprofile.reactive.messaging.Message; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Flow; - -import static io.smallrye.reactive.messaging.aws.sqs.tracing.SqsInstrumenter.SQS_OUTGOING_INSTRUMENTER; public class SqsOutgoingChannel extends SqsChannel { @@ -95,8 +96,8 @@ private void tracing(SqsOutgoingMessage message) { // possible as mentioned. Or it would be necessary to create the ids here. I do not like that. // .withMessageId("") .withConversationId(message.getSqsMetadata().getConversationId()) - // We do not set payload size. This would require to calculate it, which is less performant. - ; + // We do not set payload size. This would require to calculate it, which is less performant. + ; TracingUtils.traceOutgoing(SQS_OUTGOING_INSTRUMENTER, message, trace); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java index a2392174b7..fd263d06ee 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/ReceiveMessageAction.java @@ -1,5 +1,7 @@ package io.smallrye.reactive.messaging.aws.sqs.action; +import java.util.List; + import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; @@ -7,8 +9,6 @@ import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; -import java.util.List; - /** * AWS Documentation */ @@ -20,7 +20,6 @@ public static Uni receiveMessages( final SqsConnectorIncomingConfiguration config = clientHolder.getConfig(); - final ReceiveMessageRequest request = ReceiveMessageRequest.builder() .queueUrl(target.getTargetUrl()) .maxNumberOfMessages(config.getMaxNumberOfMessages()) From c9a77983de11f21e10df6d202222d9b665b9dce8 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:54:06 +0100 Subject: [PATCH 09/14] Add the first tests to test incoming and outgoing messaging. A lot of bug fixes and config adjustments to make it work. Start with graceful shutdown, which was needed for the tests to prevent localstack from connection loss. Well only needed for faster tests with one localstack instance. New test module to provide common aws specific logs. Except the WeldTestBase, which seems to be a duplicated multiple times. --- smallrye-reactive-messaging-aws/pom.xml | 75 ++++++++- .../serialization/SerializationResolver.java | 3 + .../pom.xml | 17 +- .../messaging/aws/sqs/SqsConnector.java | 23 ++- .../messaging/aws/sqs/SqsIncomingChannel.java | 49 ++++-- .../messaging/aws/sqs/SqsOutgoingChannel.java | 12 +- .../aws/sqs/action/SendMessageAction.java | 4 +- .../sqs/action/SendMessageBatchAction.java | 4 +- .../aws/sqs/client/SqsClientFactory.java | 28 +++- .../messaging/aws/sqs/i18n/SqsLogging.java | 4 + .../aws/sqs/message/SqsIncomingMessage.java | 11 +- .../message/SqsIncomingMessageMetadata.java | 12 ++ .../aws/sqs/SqsConnectorIncomingTest.java | 104 ++++++++++++ .../aws/sqs/SqsConnectorOutgoingTest.java | 69 ++++++++ .../messaging/aws/sqs/base/SqsTestBase.java | 152 ++++++++++++++++++ .../pom.xml | 65 ++++++++ .../messaging/aws/base/JbossLogConsumer.java | 42 +++++ .../messaging/aws/base/TestMapping.java | 23 +++ .../messaging/aws/base/WeldTestBase.java | 137 ++++++++++++++++ .../src/main/resources/log4j.properties | 7 + 20 files changed, 791 insertions(+), 50 deletions(-) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/JbossLogConsumer.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/TestMapping.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/resources/log4j.properties diff --git a/smallrye-reactive-messaging-aws/pom.xml b/smallrye-reactive-messaging-aws/pom.xml index 2f99c00976..3bf3a7e8e8 100644 --- a/smallrye-reactive-messaging-aws/pom.xml +++ b/smallrye-reactive-messaging-aws/pom.xml @@ -17,12 +17,13 @@ pom - 2.20.157 + 2.21.37 - smallrye-reactive-messaging-aws-sqs smallrye-reactive-messaging-aws-core + smallrye-reactive-messaging-aws-test + smallrye-reactive-messaging-aws-sqs @@ -44,6 +45,15 @@ ${project.version} + + + io.smallrye.reactive + smallrye-connector-attribute-processor + ${project.version} + provided + + + io.smallrye.config smallrye-config @@ -51,9 +61,66 @@ io.smallrye.reactive - smallrye-connector-attribute-processor + test-common ${project.version} - provided + test + + + org.jboss.weld.se + weld-se-shaded + ${weld.version} + test + + + org.jboss.weld + weld-core-impl + ${weld.version} + test + + + + + + + + org.testcontainers + testcontainers + test + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + org.testcontainers + localstack + ${testcontainers.version} + test + + + + org.slf4j + slf4j-log4j12 + test + + + + jakarta.json + jakarta.json-api + test + + + jakarta.json.bind + jakarta.json.bind-api + test + + + org.eclipse + yasson + ${yasson.version} + test diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java index eaf41c7ac8..b3b3ec8696 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/SerializationResolver.java @@ -25,6 +25,9 @@ public static Serializer resolveSerializer(Instance messageSerialize private static Serializer defaultSerializer(JsonMapping jsonMapping) { return payload -> { if (jsonMapping != null) { + if (payload instanceof String) { + return (String) payload; + } return jsonMapping.toJson(payload); } else { return String.valueOf(payload); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml index 22d4ac77d8..1a4fc6d71c 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml @@ -19,6 +19,13 @@ smallrye-reactive-messaging-aws-core ${project.version} + + ${project.groupId} + smallrye-reactive-messaging-aws-test + ${project.version} + test + + software.amazon.awssdk sqs @@ -43,11 +50,11 @@ netty-nio-client true - - software.amazon.awssdk - aws-crt-client - true - + + + + + io.smallrye.reactive diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index ce84d2e362..4a30946594 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -48,6 +48,9 @@ @ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") @ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "endpoint-override", type = "string", direction = INCOMING_AND_OUTGOING, description = "Configure the endpoint with which the SDK should communicate.") +@ConnectorAttribute(name = "region", type = "string", direction = INCOMING_AND_OUTGOING, description = "Configure the region with which the SDK should communicate.") + @ConnectorAttribute(name = "queue-resolver.queue-owner-aws-account-id", type = "string", direction = INCOMING_AND_OUTGOING, description = "During queue url resolving it is possible to overwrite the queue owner.") @ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") @@ -67,7 +70,7 @@ // outgoing @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") -// incomming +// incoming @ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") @ConnectorAttribute(name = "wait-time-seconds", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) for which the call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than WaitTimeSeconds. If no messages are available and the wait time expires, the call returns successfully with an empty list of messages. Default 20s.", defaultValue = "20") @ConnectorAttribute(name = "visibility-timeout", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) that the received messages are hidden from subsequent retrieve requests after being retrieved by a request. Default 15s.", defaultValue = "15") @@ -76,10 +79,9 @@ public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { - static final String CONNECTOR_NAME = "smallrye-aws-sqs"; + public static final String CONNECTOR_NAME = "smallrye-aws-sqs"; private final Map clients = new ConcurrentHashMap<>(); - private final Map clientsByChannel = new ConcurrentHashMap<>(); private final List channels = new CopyOnWriteArrayList<>(); @Inject @@ -123,14 +125,13 @@ public Flow.Publisher> getPublisher(Config config) { SqsConnectorIncomingConfiguration ic = new SqsConnectorIncomingConfiguration(config); SqsAsyncClient client = clients.computeIfAbsent(ic.getChannel(), ignored -> createSqsClient(ic, vertx)); - clientsByChannel.put(ic.getChannel(), client); - final Serializer serializer = resolveSerializer(messageSerializer, - ic.getSerializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); + final Deserializer deserializer = resolveDeserializer(messageDeserializer, ic + .getSerializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); try { SqsIncomingChannel channel = new SqsIncomingChannel( - new SqsClientHolder<>(client, vertx, ic, targetResolver, serializer, null)); + new SqsClientHolder<>(client, vertx, ic, targetResolver, null, deserializer)); channels.add(channel); return channel.getPublisher(); } catch (SqsException e) { @@ -143,14 +144,13 @@ public Flow.Subscriber> getSubscriber(Config config) { SqsConnectorOutgoingConfiguration oc = new SqsConnectorOutgoingConfiguration(config); SqsAsyncClient client = clients.computeIfAbsent(oc.getChannel(), ignored -> createSqsClient(oc, vertx)); - clientsByChannel.put(oc.getChannel(), client); - final Deserializer deserializer = resolveDeserializer(messageDeserializer, oc - .getSerializationIdentifier().orElse(oc.getChannel()), oc.getChannel(), jsonMapping); + final Serializer serializer = resolveSerializer(messageSerializer, + oc.getSerializationIdentifier().orElse(oc.getChannel()), oc.getChannel(), jsonMapping); try { SqsOutgoingChannel channel = new SqsOutgoingChannel( - new SqsClientHolder<>(client, vertx, oc, targetResolver, null, deserializer)); + new SqsClientHolder<>(client, vertx, oc, targetResolver, serializer, null)); channels.add(channel); return channel.getSubscriber(); } catch (SqsException e) { @@ -170,6 +170,5 @@ public void terminate( } channels.clear(); clients.clear(); - clientsByChannel.clear(); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java index 7cd7b4ba62..a55ec9ad6d 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -2,9 +2,11 @@ import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToList; import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; import java.util.List; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import org.eclipse.microprofile.reactive.messaging.Message; @@ -23,19 +25,25 @@ public class SqsIncomingChannel extends SqsChannel { // TODO: remove if not needed. private final EventLoopContext context; + private final SqsClientHolder clientHolder; + private final Flow.Publisher> publisher; + private final AtomicInteger counter = new AtomicInteger(0); + public SqsIncomingChannel(SqsClientHolder clientHolder) { + this.clientHolder = clientHolder; this.context = ((VertxInternal) clientHolder.getVertx().getDelegate()).createEventLoopContext(); // For performance reasons created outside the stream. - final Supplier> receiveMessagesSupplier = receiveMessagesSupplier(clientHolder); + final Supplier> receiveMessagesSupplier = receiveMessagesSupplier(); this.publisher = Multi.createBy().repeating() .uni(receiveMessagesSupplier::get) - .until(ignore -> isClosed()) + .until(ignore -> isClosed() && counter.get() == 0) + .onItem().invoke(counter::decrementAndGet) .skip().where(response -> !response.hasMessages()) - .onItem().transformToMulti(SqsIncomingChannel::createMultiOfMessages).merge() + .onItem().transformToMultiAndConcatenate(this::createMultiOfMessages) .onItem().transformToUniAndConcatenate(this::initCallbacks) // TODO: I think we do not need it in case the AWS SDK client is running on the netty eventloop thread. @@ -46,8 +54,7 @@ public SqsIncomingChannel(SqsClientHolder cli .plug(ContextOperator::apply); } - private static Supplier> receiveMessagesSupplier( - SqsClientHolder clientHolder) { + private Supplier> receiveMessagesSupplier() { // TODO: is there an easier way to get list and map configuration? Otherwise, it is important to make // sure that the data is not parsed too often. :( final List attributeNames = parseToList(clientHolder.getConfig().getAttributeNames()); @@ -55,20 +62,44 @@ private static Supplier> receiveMessagesSupplier( return () -> clientHolder.getTargetResolver().resolveTarget(clientHolder) .onItem() - .transformToUni(target -> receiveMessages(clientHolder, target, attributeNames, messageAttributeNames)); + .transformToUni(target -> { + if (isClosed()) { + return Uni.createFrom().item(ReceiveMessageResponse.builder().build()); + } + counter.incrementAndGet(); + return receiveMessages(clientHolder, target, attributeNames, messageAttributeNames); + }); } - private static Multi createMultiOfMessages(ReceiveMessageResponse response) { + private Multi> createMultiOfMessages(ReceiveMessageResponse response) { + // TODO: This is not good enough. We should wait for processing of messages. + // also check the stream with until condition. Does this skip the last messages? Or is it checked after + // processing? I think the later. return Multi.createFrom().iterable(response.messages()) .onItem().transform(SqsIncomingMessage::from); } - private Uni initCallbacks(SqsIncomingMessage msg) { + private Uni> initCallbacks(SqsIncomingMessage msg) { return Uni.createFrom().item(msg); } public void close() { - // TODO: close + // TODO: close gracefully + super.close(); + // TODO: What can we do here? Check again the Quarkus impl regarding graceful shutdown and how this worked + // and blocked the shutdown. + while (counter.get() != 0) { + log.shutdownProgress( + counter.get(), + clientHolder.getConfig().getQueue().orElse(clientHolder.getConfig().getChannel()) + ); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + throw new RuntimeException(e); + } + } } public Flow.Publisher> getPublisher() { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java index 0817097380..1627381614 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsOutgoingChannel.java @@ -28,10 +28,10 @@ public class SqsOutgoingChannel extends SqsChannel { public SqsOutgoingChannel(SqsClientHolder clientHolder) { this.clientHolder = clientHolder; - if (clientHolder.getConfig().getSendBatchEnabled()) { - subscriber = MultiUtils.via(this::createStream); - } else { + if (Boolean.TRUE.equals(clientHolder.getConfig().getSendBatchEnabled())) { subscriber = MultiUtils.via(this::createStreamWithBatching); + } else { + subscriber = MultiUtils.via(this::createStream); } } @@ -55,7 +55,7 @@ private Multi createStreamWithBatching(Multi> m) { return prepare(m) .group().by(SqsMessage::getTarget) .onItem().transformToMultiAndMerge(group -> group - .group().intoLists().of(10, Duration.ofMillis(0)) + .group().intoLists().of(10, Duration.ofMillis(3000)) .onItem().transform(msg -> Tuple2.of(group.key(), msg))) .onItem().transformToUniAndConcatenate(tuple -> sendBatchMessage(tuple.getItem1(), tuple.getItem2())); } @@ -69,9 +69,7 @@ private Uni> addTargetInformation(SqsOutgoingMes } private Uni sendMessage(SqsOutgoingMessage message) { - final SqsOutgoingMessage sqsMessage = SqsOutgoingMessage.from(message); - return clientHolder.getTargetResolver().resolveTarget(clientHolder) - .onItem().transformToUni(target -> SendMessageAction.sendMessage(clientHolder, sqsMessage)); + return SendMessageAction.sendMessage(clientHolder, message); } private Uni sendBatchMessage(SqsTarget target, List> messages) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java index 39bc4eb30e..09f0e2bf71 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -17,7 +17,7 @@ public class SendMessageAction { public static Uni sendMessage( final SqsClientHolder clientHolder, final SqsOutgoingMessage message) { - String payload = clientHolder.getSerializer().serialize(message); + String payload = clientHolder.getSerializer().serialize(message.getPayload()); SendMessageRequest request = SendMessageRequest.builder() .queueUrl(message.getTarget().getTargetUrl()) @@ -41,7 +41,7 @@ public static Uni sendMessage( // TODO: configurable retry if (true) { uni = uni.onFailure().retry() - .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) + .withBackOff(Duration.ofMillis(10), Duration.ofMillis(100)) .atMost(3); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java index cad72ce3c0..56f0a72830 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java @@ -43,7 +43,7 @@ public static Uni sendMessage( // retries for the failed once immediately until all are successful or max retries reached. if (true) { uni = uni.onFailure().retry() - .withBackOff(Duration.ofMillis(0), Duration.ofMillis(0)) + .withBackOff(Duration.ofMillis(10), Duration.ofMillis(100)) .atMost(3); } @@ -59,7 +59,7 @@ private static SendMessageBatchRequest createRequest( Map entryMap = new HashMap<>(); messages.forEach(msg -> { - String payload = clientHolder.getSerializer().serialize(msg); + String payload = clientHolder.getSerializer().serialize(msg.getPayload()); String id = UUID.randomUUID().toString(); final SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java index 3d1c965030..a55dd6464b 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java @@ -1,12 +1,18 @@ package io.smallrye.reactive.messaging.aws.sqs.client; +import java.net.URI; +import java.net.URISyntaxException; + import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorCommonConfiguration; import io.vertx.core.impl.EventLoopContext; import io.vertx.core.impl.VertxInternal; import io.vertx.mutiny.core.Vertx; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; +import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.SqsAsyncClientBuilder; @@ -32,12 +38,26 @@ public static SqsAsyncClient createSqsClient(SqsConnectorCommonConfiguration con final EventLoopContext context = ((VertxInternal) vertx.getDelegate()).createEventLoopContext(); if (nettyExists()) { - builder.asyncConfiguration(b -> b - .advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, context.nettyEventLoop())) - .httpClientBuilder(NettyNioAsyncHttpClient.builder() - .eventLoopGroup(SdkEventLoopGroup.create(context.nettyEventLoop()))); + builder.asyncConfiguration(b -> b.advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, + context.nettyEventLoop().parent())).httpClientBuilder(NettyNioAsyncHttpClient.builder() + .eventLoopGroup(SdkEventLoopGroup.create(context.nettyEventLoop().parent()))); } + config.getEndpointOverride().ifPresent(endpointOverride -> { + try { + builder.endpointOverride(new URI(endpointOverride)); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + }); + + config.getRegion().ifPresent(region -> builder.region(Region.of(region))); + + // TODO: Yeah this is where it starts to get ugly. There are so many different ways to configure it + // Sometimes you even might want to do role chaining etc, which makes it even more complicated. + // I would basically recreate Quarkiverses approach here. Or maybe inject the client directly? + builder.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test"))); + return builder.build(); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java index dec83ca17b..3212c08526 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java @@ -15,4 +15,8 @@ public interface SqsLogging extends BasicLogger { @LogMessage(level = Logger.Level.WARN) @Message(id = 19002, value = "Unable to close Sqs client") void unableToCloseClient(@Cause Throwable t); + + @LogMessage(level = Logger.Level.INFO) + @Message(id = 15201, value = "Shutdown in progress. Waiting for %d open requests on queue %s.") + void shutdownProgress(int openRequests, String queue); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java index 2d7a137b55..61a73d4f59 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java @@ -2,14 +2,15 @@ import software.amazon.awssdk.services.sqs.model.Message; -public class SqsIncomingMessage extends SqsMessage { +public class SqsIncomingMessage extends SqsMessage { - private SqsIncomingMessage(Message payload, SqsIncomingMessageMetadata metadata) { + private SqsIncomingMessage(T payload, SqsIncomingMessageMetadata metadata) { super(payload, metadata); } - public static SqsIncomingMessage from(Message payload) { - final SqsIncomingMessageMetadata sqsMetaData = new SqsIncomingMessageMetadata(); - return new SqsIncomingMessage(payload, sqsMetaData); + public static SqsIncomingMessage from(Message msg) { + // TODO: deserialize + final SqsIncomingMessageMetadata sqsMetaData = new SqsIncomingMessageMetadata(msg); + return new SqsIncomingMessage<>(msg.body(), sqsMetaData); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java index 5040f9122b..9ffbb24b37 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java @@ -1,4 +1,16 @@ package io.smallrye.reactive.messaging.aws.sqs.message; +import software.amazon.awssdk.services.sqs.model.Message; + public class SqsIncomingMessageMetadata extends SqsMessageMetadata { + + private Message msg; + + public SqsIncomingMessageMetadata(Message msg) { + this.msg = msg; + } + + public Message getMsg() { + return msg; + } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java new file mode 100644 index 0000000000..fa920aaa5c --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java @@ -0,0 +1,104 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.eclipse.microprofile.reactive.messaging.Message; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.base.SqsTestBase; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessageMetadata; +import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class SqsConnectorIncomingTest extends SqsTestBase { + + @Test + void should_receive_message_string() { + // given + MapBasedConfig config = getIncomingConfig(); + + TestApp app = runApplication(config, TestApp.class); + + sendMessage("test"); + + // when + await().untilAsserted(() -> { + List received = app.received(); + + // then + assertThat(received).hasSize(1); + assertThat(received.get(0)).isEqualTo("test"); + }); + } + + @Test + void should_receive_message_msg() { + // given + MapBasedConfig config = getIncomingConfig(); + + TestAppMsg app = runApplication(config, TestAppMsg.class); + + sendMessage("test"); + + // when + await().untilAsserted(() -> { + List> received = app.received(); + + // then + assertThat(received).hasSize(1); + + final Message stringMessage = received.get(0); + assertThat(stringMessage.getPayload()).isEqualTo("test"); + + final SqsIncomingMessageMetadata metadata = + stringMessage.getMetadata(SqsIncomingMessageMetadata.class).orElse(null); + + assertThat(metadata).isNotNull(); + assertThat(metadata.getMsg()).isNotNull(); + assertThat(metadata.getMsg().messageId()).isNotBlank(); + }); + } + + @ApplicationScoped + public static class TestApp { + + List received = new CopyOnWriteArrayList<>(); + + @Incoming(QUEUE_NAME) + void consume(String msg) { + received.add(msg); + } + + public List received() { + return received; + } + } + + @ApplicationScoped + public static class TestAppMsg { + + List> received = new CopyOnWriteArrayList<>(); + + @Incoming(QUEUE_NAME) + Uni consume(Message msg) { + received.add(msg); + return Uni.createFrom().completionStage(msg.ack()); + } + + public List> received() { + return received; + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java new file mode 100644 index 0000000000..3e5ee518fd --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java @@ -0,0 +1,69 @@ +package io.smallrye.reactive.messaging.aws.sqs; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.reactive.messaging.Channel; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import io.smallrye.reactive.messaging.MutinyEmitter; +import io.smallrye.reactive.messaging.aws.sqs.base.SqsTestBase; +import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class SqsConnectorOutgoingTest extends SqsTestBase { + + @Test + void should_send_message_without_batching() { + // given + MapBasedConfig config = getOutgoingConfig(); + + TestApp app = runApplication(config, TestApp.class); + + // when + app.sendMessage("test").subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); + + // then + List messages = receiveMessages(); + assertThat(messages).hasSize(1); + assertThat(messages.get(0)).isEqualTo("test"); + } + + @Test + void should_send_message_with_batching() { + // given + MapBasedConfig config = getOutgoingConfig().with("mp.messaging.outgoing.test.send.batch.enabled", true); + + TestApp app = runApplication(config, TestApp.class); + + // when + app.sendMessage("test").subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); + + // then + List messages = receiveMessages(); + assertThat(messages).hasSize(1); + assertThat(messages.get(0)).isEqualTo("test"); + } + + @ApplicationScoped + public static class TestApp { + + //List received = new CopyOnWriteArrayList<>(); + + @Inject + @Channel(QUEUE_NAME) + MutinyEmitter emitter; + + Uni sendMessage(String msg) { + return emitter.send(msg); + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java new file mode 100644 index 0000000000..7b3fe04b19 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java @@ -0,0 +1,152 @@ +package io.smallrye.reactive.messaging.aws.sqs.base; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.eclipse.microprofile.reactive.messaging.spi.ConnectorLiteral; +import org.jboss.logging.Logger; +import org.jboss.weld.environment.se.Weld; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import io.smallrye.reactive.messaging.aws.base.JbossLogConsumer; +import io.smallrye.reactive.messaging.aws.base.WeldTestBase; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnector; +import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; +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.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; + +@Testcontainers +public class SqsTestBase extends WeldTestBase { + + // TODO: Annotation seems to have a racing condition issue with BeforeAll... + // so maybe singleton pattern? + //@Container + protected static final LocalStackContainer LOCAL_STACK_CONTAINER = + new LocalStackContainer(DockerImageName.parse("localstack/localstack:latest")) + .withServices( + LocalStackContainer.Service.STS, LocalStackContainer.Service.SQS) + .withEnv("SKIP_SSL_CERT_DOWNLOAD", "1") + .withEnv("DISABLE_EVENTS", "1") + .withEnv("DNS_ADDRESS", "0") + .withEnv("DEBUG", "1") + .withEnv("LS_LOG", "trace"); + + static { + // https://java.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers + // Ryuk will stop it. + LOCAL_STACK_CONTAINER.start(); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private static SqsAsyncClient CLIENT; + protected static final String QUEUE_NAME = "test"; + + @BeforeAll + static void baseBeforeAll() throws ExecutionException, InterruptedException { + LOCAL_STACK_CONTAINER.followOutput(new JbossLogConsumer(Logger.getLogger(SqsTestBase.class))); + + CLIENT = SqsAsyncClient.builder() + .endpointOverride(LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) + .region(Region.of(LOCAL_STACK_CONTAINER.getRegion())) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test"))) + .build(); + CLIENT.createQueue(CreateQueueRequest.builder().queueName(QUEUE_NAME).build()).get(); + } + + @Override + protected void registerBeanClasses(final Weld weld) { + weld.addBeanClass(SqsConnector.class); + } + + @Override + protected void close() { + getBeanManager().createInstance() + .select(SqsConnector.class, ConnectorLiteral.of(SqsConnector.CONNECTOR_NAME)) + .get().terminate(null); + } + + protected MapBasedConfig getOutgoingConfig() { + return new MapBasedConfig() + .with("mp.messaging.outgoing.test.endpoint-override", + LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) + .with("mp.messaging.outgoing.test.region", LOCAL_STACK_CONTAINER.getRegion()) + .with("mp.messaging.outgoing.test.connector", SqsConnector.CONNECTOR_NAME); + } + + protected MapBasedConfig getIncomingConfig() { + return new MapBasedConfig() + .with("mp.messaging.incoming.test.endpoint-override", + LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) + .with("mp.messaging.incoming.test.region", LOCAL_STACK_CONTAINER.getRegion()) + .with("mp.messaging.incoming.test.connector", SqsConnector.CONNECTOR_NAME) + .with("mp.messaging.incoming.test.wait-time-seconds",2) + //.with("mp.messaging.incoming.test.visibility-timeout",2) + ; + } + + @BeforeEach + void baseBeforeEach() throws ExecutionException, InterruptedException { + getQueueUrl(QUEUE_NAME).thenCompose( + queueUrl -> CLIENT.purgeQueue(PurgeQueueRequest.builder().queueUrl(queueUrl).build())).get(); + } + + private CompletableFuture getQueueUrl(String queueName) { + return CLIENT.getQueueUrl(GetQueueUrlRequest.builder().queueName(queueName).build()) + .thenApply(GetQueueUrlResponse::queueUrl); + } + + protected void sendMessage(String message) { + sendMessage(QUEUE_NAME, message); + } + + protected void sendMessage(String queueName, String message) { + try { + getQueueUrl(queueName).thenCompose(queueUrl -> CLIENT. + sendMessage(SendMessageRequest.builder() + .queueUrl(queueUrl) + .messageBody(message).build())).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + protected List receiveMessages() { + return receiveMessages(QUEUE_NAME); + } + + protected List receiveMessages(String queueName) { + try { + final ReceiveMessageResponse response = getQueueUrl(queueName).thenCompose( + queueUrl -> CLIENT.receiveMessage(b -> b.queueUrl(queueUrl).waitTimeSeconds(10).build())) + .get(); + + if (!response.hasMessages()) { + return Collections.emptyList(); + } + + return response.messages().stream().map(Message::body).collect(Collectors.toList()); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml new file mode 100644 index 0000000000..3716b69ee0 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + io.smallrye.reactive + smallrye-reactive-messaging-aws + 4.11.0-SNAPSHOT + + + smallrye-reactive-messaging-aws-test + + SmallRye Reactive Messaging : Connector :: AWS Test + + + + io.smallrye.reactive + test-common + ${project.version} + provided + + + io.smallrye.config + smallrye-config + provided + + + org.jboss.weld.se + weld-se-shaded + ${weld.version} + provided + + + org.jboss.weld + weld-core-impl + ${weld.version} + provided + + + + org.testcontainers + testcontainers + provided + + + + org.junit.jupiter + junit-jupiter-api + provided + + + + jakarta.json + jakarta.json-api + provided + + + jakarta.json.bind + jakarta.json.bind-api + provided + + + + diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/JbossLogConsumer.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/JbossLogConsumer.java new file mode 100644 index 0000000000..7f3e7836d7 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/JbossLogConsumer.java @@ -0,0 +1,42 @@ +package io.smallrye.reactive.messaging.aws.base; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.output.BaseConsumer; +import org.testcontainers.containers.output.OutputFrame; + +public class JbossLogConsumer extends BaseConsumer { + + private final Logger logger; + + private String prefix = ""; + + public JbossLogConsumer(final Logger logger) { + this.logger = logger; + } + + public JbossLogConsumer withPrefix(String prefix) { + this.prefix = "\uD83D\uDC33 [" + prefix + "] "; + return this; + } + + @Override + public void accept(final OutputFrame outputFrame) { + final OutputFrame.OutputType outputType = outputFrame.getType(); + + String utf8String = outputFrame.getUtf8String(); + utf8String = utf8String.replaceAll("((\r?\n)|(\r))$", ""); + + switch (outputType) { + case END: + break; + case STDOUT: + logger.infov("{0}{1}: {2}", prefix, outputType, utf8String); + break; + case STDERR: + logger.errorv("{0}{1}: {2}", prefix, outputType, utf8String); + break; + default: + throw new IllegalArgumentException("Unexpected outputType " + outputType); + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/TestMapping.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/TestMapping.java new file mode 100644 index 0000000000..3bb9ea62f1 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/TestMapping.java @@ -0,0 +1,23 @@ +package io.smallrye.reactive.messaging.aws.base; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import io.smallrye.reactive.messaging.json.JsonMapping; + +@ApplicationScoped +public class TestMapping implements JsonMapping { + + private Jsonb jsonb = JsonbBuilder.create(); + + @Override + public String toJson(Object object) { + return jsonb.toJson(object); + } + + @Override + public T fromJson(String str, Class type) { + return jsonb.fromJson(str, type); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java new file mode 100644 index 0000000000..64941d58db --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java @@ -0,0 +1,137 @@ +package io.smallrye.reactive.messaging.aws.base; + +import jakarta.enterprise.inject.spi.BeanManager; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.weld.environment.se.Weld; +import org.jboss.weld.environment.se.WeldContainer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import io.smallrye.config.SmallRyeConfigProviderResolver; +import io.smallrye.config.inject.ConfigExtension; +import io.smallrye.reactive.messaging.providers.MediatorFactory; +import io.smallrye.reactive.messaging.providers.connectors.ExecutionHolder; +import io.smallrye.reactive.messaging.providers.connectors.WorkerPoolRegistry; +import io.smallrye.reactive.messaging.providers.extension.ChannelProducer; +import io.smallrye.reactive.messaging.providers.extension.EmitterFactoryImpl; +import io.smallrye.reactive.messaging.providers.extension.HealthCenter; +import io.smallrye.reactive.messaging.providers.extension.LegacyEmitterFactoryImpl; +import io.smallrye.reactive.messaging.providers.extension.MediatorManager; +import io.smallrye.reactive.messaging.providers.extension.MutinyEmitterFactoryImpl; +import io.smallrye.reactive.messaging.providers.extension.ReactiveMessagingExtension; +import io.smallrye.reactive.messaging.providers.impl.ConfiguredChannelFactory; +import io.smallrye.reactive.messaging.providers.impl.ConnectorFactories; +import io.smallrye.reactive.messaging.providers.impl.InternalChannelRegistry; +import io.smallrye.reactive.messaging.providers.metrics.MetricDecorator; +import io.smallrye.reactive.messaging.providers.metrics.MicrometerDecorator; +import io.smallrye.reactive.messaging.providers.wiring.Wiring; +import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; + +public abstract class WeldTestBase { + + protected Weld weld; + protected WeldContainer container; + + @BeforeEach + public void initWeld() { + weld = new Weld(); + + // SmallRye config + ConfigExtension extension = new ConfigExtension(); + weld.addExtension(extension); + + weld.addBeanClass(MediatorFactory.class); + weld.addBeanClass(MediatorManager.class); + weld.addBeanClass(InternalChannelRegistry.class); + weld.addBeanClass(ConnectorFactories.class); + weld.addBeanClass(ConfiguredChannelFactory.class); + weld.addBeanClass(ChannelProducer.class); + weld.addBeanClass(ExecutionHolder.class); + weld.addBeanClass(WorkerPoolRegistry.class); + weld.addBeanClass(HealthCenter.class); + weld.addBeanClass(Wiring.class); + weld.addExtension(new ReactiveMessagingExtension()); + + weld.addBeanClass(EmitterFactoryImpl.class); + weld.addBeanClass(MutinyEmitterFactoryImpl.class); + weld.addBeanClass(LegacyEmitterFactoryImpl.class); + weld.addBeanClass(TestMapping.class); + + registerBeanClasses(weld); + weld.addBeanClass(MetricDecorator.class); + weld.addBeanClass(MicrometerDecorator.class); + weld.disableDiscovery(); + } + + protected void registerBeanClasses(Weld weld) { + // overwrite if needed. + } + + protected abstract void close(); + + @AfterEach + public void stopContainer() { + close(); + if (container != null) { + container.close(); + } + // Release the config objects + SmallRyeConfigProviderResolver.instance().releaseConfig(ConfigProvider.getConfig()); + } + + public BeanManager getBeanManager() { + if (container == null) { + throw new IllegalStateException("Application not started"); + } + return container.getBeanManager(); + } + + public T get(Class clazz) { + return getBeanManager().createInstance().select(clazz).get(); + } + + public T runApplication(MapBasedConfig config, Class clazz) { + weld.addBeanClass(clazz); + runApplication(config); + return get(clazz); + } + + public void runApplication(MapBasedConfig config) { + if (config != null) { + config.write(); + } else { + MapBasedConfig.cleanup(); + } + + container = weld.initialize(); + } + + public static void addConfig(MapBasedConfig config) { + if (config != null) { + config.write(); + } else { + MapBasedConfig.cleanup(); + } + } + + public HealthCenter getHealth() { + if (container == null) { + throw new IllegalStateException("Application not started"); + } + return container.getBeanManager().createInstance().select(HealthCenter.class).get(); + } + + public boolean isStarted() { + return getHealth().getStartup().isOk(); + } + + public boolean isReady() { + return getHealth().getReadiness().isOk(); + } + + public boolean isAlive() { + return getHealth().getLiveness().isOk(); + } + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/resources/log4j.properties b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/resources/log4j.properties new file mode 100644 index 0000000000..4019c5c2d6 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=INFO, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n + +log4j.logger.org.jboss.weld=WARN From a75dd1aeb2979c55856da88ba66e67ef3270898a Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:25:54 +0100 Subject: [PATCH 10/14] Fix testcontainer issues --- .../messaging/aws/sqs/base/SqsTestBase.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java index 7b3fe04b19..cd8caf555a 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java @@ -1,7 +1,10 @@ package io.smallrye.reactive.messaging.aws.sqs.base; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -11,10 +14,19 @@ import org.jboss.weld.environment.se.Weld; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.ExecInContainerPattern; import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.containers.startupcheck.StartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.ShellStrategy; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.InspectContainerResponse; + import io.smallrye.reactive.messaging.aws.base.JbossLogConsumer; import io.smallrye.reactive.messaging.aws.base.WeldTestBase; import io.smallrye.reactive.messaging.aws.sqs.SqsConnector; @@ -28,14 +40,13 @@ import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; import software.amazon.awssdk.services.sqs.model.Message; import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; @Testcontainers public class SqsTestBase extends WeldTestBase { - // TODO: Annotation seems to have a racing condition issue with BeforeAll... - // so maybe singleton pattern? //@Container protected static final LocalStackContainer LOCAL_STACK_CONTAINER = new LocalStackContainer(DockerImageName.parse("localstack/localstack:latest")) @@ -45,21 +56,20 @@ public class SqsTestBase extends WeldTestBase { .withEnv("DISABLE_EVENTS", "1") .withEnv("DNS_ADDRESS", "0") .withEnv("DEBUG", "1") - .withEnv("LS_LOG", "trace"); + .withEnv("LS_LOG", "trace") + // Due to latest changes the container is in rdy state too early, which can create connection + // issues. So we wait until it is really rdy. + .waitingFor(new ShellStrategy().withCommand("awslocal sqs list-queues")); static { // https://java.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers // Ryuk will stop it. LOCAL_STACK_CONTAINER.start(); - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } } private static SqsAsyncClient CLIENT; protected static final String QUEUE_NAME = "test"; + protected static final String FIFO_QUEUE_NAME = "test.fifo"; @BeforeAll static void baseBeforeAll() throws ExecutionException, InterruptedException { @@ -71,6 +81,8 @@ static void baseBeforeAll() throws ExecutionException, InterruptedException { .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test"))) .build(); CLIENT.createQueue(CreateQueueRequest.builder().queueName(QUEUE_NAME).build()).get(); + CLIENT.createQueue(CreateQueueRequest.builder().queueName(FIFO_QUEUE_NAME) + .attributes(Map.of(QueueAttributeName.FIFO_QUEUE, "true")).build()).get(); } @Override From aaafde14737dacc10a2c146c086068eeeefe3cb1 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:28:28 +0100 Subject: [PATCH 11/14] Add ack for sqs messages via delete or batch delete. Add more tests to make sure that this works in general. --- .../messaging/aws/config/ConfigHelper.java | 4 - .../messaging/aws/sqs/SqsConnector.java | 15 ++- .../messaging/aws/sqs/SqsIncomingChannel.java | 21 ++-- .../messaging/aws/sqs/ack/SqsAckHandler.java | 54 ++++++++ .../aws/sqs/action/DeleteMessageAction.java | 19 +++ .../sqs/action/DeleteMessageBatchAction.java | 46 +++++++ .../sqs/action/SendMessageBatchAction.java | 2 +- .../aws/sqs/client/SqsClientFactory.java | 2 +- .../aws/sqs/message/SqsIncomingMessage.java | 25 +++- .../message/SqsIncomingMessageMetadata.java | 10 +- .../aws/sqs/SqsConnectorIncomingTest.java | 116 ++++++++++++++++-- .../messaging/aws/sqs/base/SqsTestBase.java | 101 ++++++++++----- 12 files changed, 346 insertions(+), 69 deletions(-) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java index 6408b95d20..2b9f33a197 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/config/ConfigHelper.java @@ -2,10 +2,6 @@ import java.util.*; -/** - * @author Christopher Holomek - * @since 01.10.2023 - */ public class ConfigHelper { public static List parseToList(String valueToParse) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index 4a30946594..faf782e7d7 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -1,5 +1,6 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING; import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.INCOMING_AND_OUTGOING; import static io.smallrye.reactive.messaging.annotations.ConnectorAttribute.Direction.OUTGOING; import static io.smallrye.reactive.messaging.aws.serialization.SerializationResolver.resolveDeserializer; @@ -71,11 +72,15 @@ @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") // incoming -@ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") -@ConnectorAttribute(name = "wait-time-seconds", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) for which the call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than WaitTimeSeconds. If no messages are available and the wait time expires, the call returns successfully with an empty list of messages. Default 20s.", defaultValue = "20") -@ConnectorAttribute(name = "visibility-timeout", type = "int", direction = ConnectorAttribute.Direction.INCOMING, description = "The duration (in seconds) that the received messages are hidden from subsequent retrieve requests after being retrieved by a request. Default 15s.", defaultValue = "15") -@ConnectorAttribute(name = "attribute-names", type = "string", direction = ConnectorAttribute.Direction.INCOMING, description = "A comma separated list of attributes that need to be returned along with each message. Default empty.", defaultValue = "") -@ConnectorAttribute(name = "message-attribute-names", type = "string", direction = ConnectorAttribute.Direction.INCOMING, description = "A comma separated list of message attributes that need to be returned along with each message. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") +@ConnectorAttribute(name = "wait-time-seconds", type = "int", direction = INCOMING, description = "The duration (in seconds) for which the call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than WaitTimeSeconds. If no messages are available and the wait time expires, the call returns successfully with an empty list of messages. Default 20s.", defaultValue = "20") +@ConnectorAttribute(name = "visibility-timeout", type = "int", direction = INCOMING, description = "The duration (in seconds) that the received messages are hidden from subsequent retrieve requests after being retrieved by a request. Default 15s.", defaultValue = "15") +@ConnectorAttribute(name = "attribute-names", type = "string", direction = INCOMING, description = "A comma separated list of attributes that need to be returned along with each message. Default empty.", defaultValue = "") +@ConnectorAttribute(name = "message-attribute-names", type = "string", direction = INCOMING, description = "A comma separated list of message attributes that need to be returned along with each message. Default empty.", defaultValue = "") + +@ConnectorAttribute(name = "delete.batch.enabled", type = "boolean", direction = INCOMING, description = "Delete/confirm messages in batches.", defaultValue = "false") +@ConnectorAttribute(name = "delete.batch.max-size", type = "int", direction = INCOMING, description = "The maximum number of messages to delete in a batch. Valid values: 1 to 10. Default: 10.", defaultValue = "10") +@ConnectorAttribute(name = "delete.batch.max-delay", type = "int", direction = INCOMING, description = "The maximum number of seconds to wait for a batch. Needs to be configured lower than message visibility-timeout. Default: 3.", defaultValue = "3") public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java index a55ec9ad6d..24ade3cbfb 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -1,5 +1,6 @@ package io.smallrye.reactive.messaging.aws.sqs; +import static io.smallrye.mutiny.helpers.spies.Spy.onItem; import static io.smallrye.reactive.messaging.aws.config.ConfigHelper.parseToList; import static io.smallrye.reactive.messaging.aws.sqs.action.ReceiveMessageAction.receiveMessages; import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsLogging.log; @@ -13,6 +14,7 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.ack.SqsAckHandler; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; import io.smallrye.reactive.messaging.providers.locals.ContextOperator; @@ -31,15 +33,21 @@ public class SqsIncomingChannel extends SqsChannel { private final AtomicInteger counter = new AtomicInteger(0); + private final SqsAckHandler ackHandler; + public SqsIncomingChannel(SqsClientHolder clientHolder) { this.clientHolder = clientHolder; this.context = ((VertxInternal) clientHolder.getVertx().getDelegate()).createEventLoopContext(); + this.ackHandler = new SqsAckHandler(clientHolder); // For performance reasons created outside the stream. final Supplier> receiveMessagesSupplier = receiveMessagesSupplier(); this.publisher = Multi.createBy().repeating() .uni(receiveMessagesSupplier::get) + // TODO: This is not good enough. We should wait for processing of messages. + // also check the stream with until condition. Does this skip the last messages? Or is it checked after + // processing? I think the later. .until(ignore -> isClosed() && counter.get() == 0) .onItem().invoke(counter::decrementAndGet) .skip().where(response -> !response.hasMessages()) @@ -72,11 +80,11 @@ private Supplier> receiveMessagesSupplier() { } private Multi> createMultiOfMessages(ReceiveMessageResponse response) { - // TODO: This is not good enough. We should wait for processing of messages. - // also check the stream with until condition. Does this skip the last messages? Or is it checked after - // processing? I think the later. - return Multi.createFrom().iterable(response.messages()) - .onItem().transform(SqsIncomingMessage::from); + final Multi> result = Multi.createFrom().iterable(response.messages()) + .onItem().transform(msg -> SqsIncomingMessage.from(msg, ackHandler)); + + return result.onItem().call(msg -> clientHolder.getTargetResolver().resolveTarget(clientHolder) + .onItem().invoke(msg::withTarget)); } private Uni> initCallbacks(SqsIncomingMessage msg) { @@ -91,8 +99,7 @@ public void close() { while (counter.get() != 0) { log.shutdownProgress( counter.get(), - clientHolder.getConfig().getQueue().orElse(clientHolder.getConfig().getChannel()) - ); + clientHolder.getConfig().getQueue().orElse(clientHolder.getConfig().getChannel())); try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java new file mode 100644 index 0000000000..33203cd255 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java @@ -0,0 +1,54 @@ +package io.smallrye.reactive.messaging.aws.sqs.ack; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.action.DeleteMessageAction; +import io.smallrye.reactive.messaging.aws.sqs.action.DeleteMessageBatchAction; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; + +public class SqsAckHandler { + + private final SqsClientHolder clientHolder; + + private MultiEmitter> emitter; + + public SqsAckHandler(final SqsClientHolder clientHolder) { + this.clientHolder = clientHolder; + + if (Boolean.TRUE.equals(clientHolder.getConfig().getDeleteBatchEnabled())) { + Multi.createFrom().> emitter(e -> emitter = e) + .group().intoLists().of(getMaxSize(clientHolder), Duration.ofSeconds(getMaxDelay(clientHolder)) + ) + .onItem().call(messages -> DeleteMessageBatchAction.deleteMessages(clientHolder, messages)) + .subscribe().with(ignored -> { + }); + } + } + + private static Integer getMaxSize( + final SqsClientHolder clientHolder) { + return Math.max(1, Math.min(clientHolder.getConfig().getDeleteBatchMaxSize(), 10)); + } + + private long getMaxDelay(final SqsClientHolder clientHolder) { + return Math.max(1, Math.min( + clientHolder.getConfig().getDeleteBatchMaxDelay(), clientHolder.getConfig().getVisibilityTimeout() - 1) + ); + } + + public CompletionStage handle(final SqsIncomingMessage msg) { + + if (Boolean.TRUE.equals(clientHolder.getConfig().getDeleteBatchEnabled())) { + emitter.emit(msg); + return CompletableFuture.completedFuture(null); + } else { + return DeleteMessageAction.deleteMessage(clientHolder, msg).subscribeAsCompletionStage(); + } + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java new file mode 100644 index 0000000000..d38aedf6f3 --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java @@ -0,0 +1,19 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; +import software.amazon.awssdk.services.sqs.model.DeleteMessageRequest; + +public class DeleteMessageAction { + + public static Uni deleteMessage( + SqsClientHolder clientHolder, SqsIncomingMessage message) { + + final DeleteMessageRequest request = DeleteMessageRequest.builder().queueUrl(message.getTarget().getTargetUrl()) + .receiptHandle(message.getSqsMetadata().getAwsMessage().receiptHandle()).build(); + + return Uni.createFrom().completionStage(clientHolder.getClient().deleteMessage(request)).replaceWithVoid(); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java new file mode 100644 index 0000000000..e7aa90150f --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java @@ -0,0 +1,46 @@ +package io.smallrye.reactive.messaging.aws.sqs.action; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorIncomingConfiguration; +import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; +import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; +import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; +import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; + +public class DeleteMessageBatchAction { + + public static Uni deleteMessages( + SqsClientHolder clientHolder, + List> messages) { + + if (messages.isEmpty()) { + return Uni.createFrom().voidItem(); + } + + Collection entries = new ArrayList<>(messages.size()); + + AtomicReference target = new AtomicReference<>(); + messages.forEach(message -> { + String id = UUID.randomUUID().toString(); + DeleteMessageBatchRequestEntry entry = DeleteMessageBatchRequestEntry.builder().id(id) + .receiptHandle(message.getSqsMetadata().getAwsMessage().receiptHandle()).build(); + + target.set(message.getTarget()); + + entries.add(entry); + + }); + + DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder().queueUrl(target.get().getTargetUrl()) + .entries(entries).build(); + + return Uni.createFrom().completionStage(clientHolder.getClient().deleteMessageBatch(request)).replaceWithVoid(); + } +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java index 56f0a72830..14d1bc8d1e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java @@ -62,7 +62,7 @@ private static SendMessageBatchRequest createRequest( String payload = clientHolder.getSerializer().serialize(msg.getPayload()); String id = UUID.randomUUID().toString(); - final SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() + SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() // in batching we need to generate the id of a message for every entry. .id(id) .messageAttributes(null) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java index a55dd6464b..918c5ded2c 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java @@ -40,7 +40,7 @@ public static SqsAsyncClient createSqsClient(SqsConnectorCommonConfiguration con if (nettyExists()) { builder.asyncConfiguration(b -> b.advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, context.nettyEventLoop().parent())).httpClientBuilder(NettyNioAsyncHttpClient.builder() - .eventLoopGroup(SdkEventLoopGroup.create(context.nettyEventLoop().parent()))); + .eventLoopGroup(SdkEventLoopGroup.create(context.nettyEventLoop().parent()))); } config.getEndpointOverride().ifPresent(endpointOverride -> { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java index 61a73d4f59..84ac429dfb 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java @@ -1,16 +1,33 @@ package io.smallrye.reactive.messaging.aws.sqs.message; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; + +import io.smallrye.reactive.messaging.aws.sqs.ack.SqsAckHandler; import software.amazon.awssdk.services.sqs.model.Message; public class SqsIncomingMessage extends SqsMessage { - private SqsIncomingMessage(T payload, SqsIncomingMessageMetadata metadata) { + private final SqsAckHandler ackHandler; + + private SqsIncomingMessage(T payload, SqsAckHandler ackHandler, SqsIncomingMessageMetadata metadata) { super(payload, metadata); + this.ackHandler = ackHandler; + } + + @Override + public CompletionStage ack() { + return ackHandler.handle(this); + } + + @Override + public Supplier> getAck() { + return () -> this.ackHandler.handle(this); } - public static SqsIncomingMessage from(Message msg) { - // TODO: deserialize + public static SqsIncomingMessage from(Message msg, SqsAckHandler ackHandler) { + // TODO: deserialize? final SqsIncomingMessageMetadata sqsMetaData = new SqsIncomingMessageMetadata(msg); - return new SqsIncomingMessage<>(msg.body(), sqsMetaData); + return new SqsIncomingMessage<>(msg.body(), ackHandler, sqsMetaData); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java index 9ffbb24b37..79c75cc753 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessageMetadata.java @@ -4,13 +4,13 @@ public class SqsIncomingMessageMetadata extends SqsMessageMetadata { - private Message msg; + private final Message awsMessage; - public SqsIncomingMessageMetadata(Message msg) { - this.msg = msg; + public SqsIncomingMessageMetadata(Message awsMessage) { + this.awsMessage = awsMessage; } - public Message getMsg() { - return msg; + public Message getAwsMessage() { + return awsMessage; } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java index fa920aaa5c..cd75b00f4c 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java @@ -4,7 +4,6 @@ import static org.awaitility.Awaitility.await; import java.util.List; -import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import jakarta.enterprise.context.ApplicationScoped; @@ -17,7 +16,6 @@ import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.aws.sqs.base.SqsTestBase; -import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessageMetadata; import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; @@ -25,7 +23,7 @@ class SqsConnectorIncomingTest extends SqsTestBase { @Test - void should_receive_message_string() { + void should_receive_message_via_payload() { // given MapBasedConfig config = getIncomingConfig(); @@ -40,11 +38,12 @@ void should_receive_message_string() { // then assertThat(received).hasSize(1); assertThat(received.get(0)).isEqualTo("test"); + verifyNoInvisibleMessages(); }); } @Test - void should_receive_message_msg() { + void should_receive_message_via_message() { // given MapBasedConfig config = getIncomingConfig(); @@ -62,12 +61,97 @@ void should_receive_message_msg() { final Message stringMessage = received.get(0); assertThat(stringMessage.getPayload()).isEqualTo("test"); - final SqsIncomingMessageMetadata metadata = - stringMessage.getMetadata(SqsIncomingMessageMetadata.class).orElse(null); + final SqsIncomingMessageMetadata metadata = stringMessage.getMetadata(SqsIncomingMessageMetadata.class) + .orElse(null); assertThat(metadata).isNotNull(); - assertThat(metadata.getMsg()).isNotNull(); - assertThat(metadata.getMsg().messageId()).isNotBlank(); + assertThat(metadata.getAwsMessage()).isNotNull(); + assertThat(metadata.getAwsMessage().messageId()).isNotBlank(); + verifyNoInvisibleMessages(); + }); + } + + @Test + void should_ack_message_with_batching() { + // given + MapBasedConfig config = getIncomingConfig() + .with("mp.messaging.incoming.test.delete.batch.enabled", true) + .with("mp.messaging.incoming.test.delete.batch.max-size", 10) + .with("mp.messaging.incoming.test.delete.batch.max-delay", 2) + .with("mp.messaging.incoming.test.visibility-timeout", 10); + + TestApp app = runApplication(config, TestApp.class); + + sendMessage("test"); + + // when + await().untilAsserted(() -> { + List received = app.received(); + + // then + assertThat(received).hasSize(1); + assertThat(received.get(0)).isEqualTo("test"); + + // test the delayed batching + verifyInvisibleMessages(1); + }); + // then + await().untilAsserted(this::verifyNoInvisibleMessages); + } + + @Test + void should_ack_messages_with_batching() { + // given + MapBasedConfig config = getIncomingConfig() + .with("mp.messaging.incoming.test.delete.batch.enabled", true) + .with("mp.messaging.incoming.test.delete.batch.max-size", 10) + .with("mp.messaging.incoming.test.delete.batch.max-delay", 2) + .with("mp.messaging.incoming.test.visibility-timeout", 10); + + TestApp app = runApplication(config, TestApp.class); + + for (int i = 0; i < 10; i++) { + sendMessage("test"); + } + + // when + await().untilAsserted(() -> { + List received = app.received(); + + // then + assertThat(received).hasSize(10); + + // test the delayed batching + verifyNoInvisibleMessages(); + }); + } + + @Test + void should_receive_message_via_message_without_ack() { + // given + MapBasedConfig config = getIncomingConfig(); + + TestAppMsgNoAck app = runApplication(config, TestAppMsgNoAck.class); + + sendMessage("test"); + + // when + await().untilAsserted(() -> { + List> received = app.received(); + + // then + assertThat(received).hasSize(1); + + final Message stringMessage = received.get(0); + assertThat(stringMessage.getPayload()).isEqualTo("test"); + + final SqsIncomingMessageMetadata metadata = stringMessage.getMetadata(SqsIncomingMessageMetadata.class) + .orElse(null); + + assertThat(metadata).isNotNull(); + assertThat(metadata.getAwsMessage()).isNotNull(); + assertThat(metadata.getAwsMessage().messageId()).isNotBlank(); + verifyInvisibleMessages(1); }); } @@ -101,4 +185,20 @@ public List> received() { return received; } } + + @ApplicationScoped + public static class TestAppMsgNoAck { + + List> received = new CopyOnWriteArrayList<>(); + + @Incoming(QUEUE_NAME) + Uni consume(Message msg) { + received.add(msg); + return Uni.createFrom().voidItem(); + } + + public List> received() { + return received; + } + } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java index cd8caf555a..7fa39c799d 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java @@ -1,7 +1,8 @@ package io.smallrye.reactive.messaging.aws.sqs.base; -import java.io.IOException; -import java.nio.charset.StandardCharsets; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.services.sqs.model.QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE; + import java.util.Collections; import java.util.List; import java.util.Map; @@ -14,19 +15,11 @@ import org.jboss.weld.environment.se.Weld; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.Container; -import org.testcontainers.containers.ExecInContainerPattern; import org.testcontainers.containers.localstack.LocalStackContainer; -import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.ShellStrategy; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.InspectContainerResponse; - import io.smallrye.reactive.messaging.aws.base.JbossLogConsumer; import io.smallrye.reactive.messaging.aws.base.WeldTestBase; import io.smallrye.reactive.messaging.aws.sqs.SqsConnector; @@ -36,6 +29,7 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; import software.amazon.awssdk.services.sqs.model.Message; @@ -47,19 +41,18 @@ @Testcontainers public class SqsTestBase extends WeldTestBase { - //@Container - protected static final LocalStackContainer LOCAL_STACK_CONTAINER = - new LocalStackContainer(DockerImageName.parse("localstack/localstack:latest")) - .withServices( + protected static final LocalStackContainer LOCAL_STACK_CONTAINER = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:latest")) + .withServices( LocalStackContainer.Service.STS, LocalStackContainer.Service.SQS) - .withEnv("SKIP_SSL_CERT_DOWNLOAD", "1") - .withEnv("DISABLE_EVENTS", "1") - .withEnv("DNS_ADDRESS", "0") - .withEnv("DEBUG", "1") - .withEnv("LS_LOG", "trace") - // Due to latest changes the container is in rdy state too early, which can create connection - // issues. So we wait until it is really rdy. - .waitingFor(new ShellStrategy().withCommand("awslocal sqs list-queues")); + .withEnv("SKIP_SSL_CERT_DOWNLOAD", "1") + .withEnv("DISABLE_EVENTS", "1") + .withEnv("DNS_ADDRESS", "0") + .withEnv("DEBUG", "1") + .withEnv("LS_LOG", "info") + // Due to latest changes the container is in rdy state too early, which can create connection + // issues. So we wait until it is really rdy. + .waitingFor(new ShellStrategy().withCommand("awslocal sqs list-queues")); static { // https://java.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers @@ -85,6 +78,17 @@ static void baseBeforeAll() throws ExecutionException, InterruptedException { .attributes(Map.of(QueueAttributeName.FIFO_QUEUE, "true")).build()).get(); } + @BeforeEach + void baseBeforeEach() throws ExecutionException, InterruptedException { + getQueueUrl(QUEUE_NAME) + .thenCompose(queueUrl -> CLIENT.purgeQueue( + PurgeQueueRequest.builder().queueUrl(queueUrl).build())) + .thenCompose(ignore -> getQueueUrl(FIFO_QUEUE_NAME) + .thenCompose(queueUrl -> CLIENT.purgeQueue( + PurgeQueueRequest.builder().queueUrl(queueUrl).build()))) + .get(); + } + @Override protected void registerBeanClasses(final Weld weld) { weld.addBeanClass(SqsConnector.class); @@ -111,15 +115,9 @@ protected MapBasedConfig getIncomingConfig() { LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) .with("mp.messaging.incoming.test.region", LOCAL_STACK_CONTAINER.getRegion()) .with("mp.messaging.incoming.test.connector", SqsConnector.CONNECTOR_NAME) - .with("mp.messaging.incoming.test.wait-time-seconds",2) - //.with("mp.messaging.incoming.test.visibility-timeout",2) - ; - } - - @BeforeEach - void baseBeforeEach() throws ExecutionException, InterruptedException { - getQueueUrl(QUEUE_NAME).thenCompose( - queueUrl -> CLIENT.purgeQueue(PurgeQueueRequest.builder().queueUrl(queueUrl).build())).get(); + .with("mp.messaging.incoming.test.wait-time-seconds", 2) + //.with("mp.messaging.incoming.test.visibility-timeout",2) + ; } private CompletableFuture getQueueUrl(String queueName) { @@ -133,8 +131,7 @@ protected void sendMessage(String message) { protected void sendMessage(String queueName, String message) { try { - getQueueUrl(queueName).thenCompose(queueUrl -> CLIENT. - sendMessage(SendMessageRequest.builder() + getQueueUrl(queueName).thenCompose(queueUrl -> CLIENT.sendMessage(SendMessageRequest.builder() .queueUrl(queueUrl) .messageBody(message).build())).get(); } catch (InterruptedException | ExecutionException e) { @@ -149,7 +146,7 @@ protected List receiveMessages() { protected List receiveMessages(String queueName) { try { final ReceiveMessageResponse response = getQueueUrl(queueName).thenCompose( - queueUrl -> CLIENT.receiveMessage(b -> b.queueUrl(queueUrl).waitTimeSeconds(10).build())) + queueUrl -> CLIENT.receiveMessage(b -> b.queueUrl(queueUrl).waitTimeSeconds(10).build())) .get(); if (!response.hasMessages()) { @@ -161,4 +158,40 @@ protected List receiveMessages(String queueName) { throw new RuntimeException(e); } } + + protected String getQueueAttribute(String queueName, QueueAttributeName attributeName) { + try { + return getQueueUrl(queueName).thenCompose(queueUrl -> + CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(attributeName) + .build()) + ).get().attributes().get(attributeName); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + protected Map getQueueAttributes(String queueName, QueueAttributeName... attributeNames) { + try { + return getQueueUrl(queueName).thenCompose(queueUrl -> + CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(attributeNames) + .build()) + ).get().attributes(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + protected void verifyNoInvisibleMessages() { + verifyInvisibleMessages(0); + } + + protected void verifyInvisibleMessages(int number) { + String msgsNotVisibile = + getQueueAttribute(QUEUE_NAME, APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE); + assertThat(msgsNotVisibile).isEqualTo(String.valueOf(number)); + } } From 0861cf02552239d055ac3ad4fa9165a9cc74c4e8 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:23:35 +0100 Subject: [PATCH 12/14] Cleanup. Add deserialization to incoming messages. Add a test, which uses a custom deserializer, which is then registered via Identifier annotation. I am not sure if this is worth it. In theory deserialization could be added in the invoked method anyway. Same for serialization. Although it is nice to have an included solution the overhead here is quite high. --- .../aws/serialization/Deserializer.java | 2 +- .../aws/serialization/Serializer.java | 2 +- .../messaging/aws/sqs/SqsConnector.java | 22 ++--- .../messaging/aws/sqs/SqsIncomingChannel.java | 2 +- .../messaging/aws/sqs/ack/SqsAckHandler.java | 6 +- .../aws/sqs/action/CreateQueueAction.java | 6 +- .../aws/sqs/action/DeleteMessageAction.java | 5 ++ .../sqs/action/DeleteMessageBatchAction.java | 5 ++ .../aws/sqs/action/SendMessageAction.java | 17 ++-- .../sqs/action/SendMessageBatchAction.java | 17 ++-- .../aws/sqs/client/SqsClientFactory.java | 2 +- .../aws/sqs/client/SqsClientHolder.java | 1 + .../messaging/aws/sqs/i18n/SqsExceptions.java | 13 +-- .../messaging/aws/sqs/i18n/SqsLogging.java | 1 + .../messaging/aws/sqs/i18n/SqsMessages.java | 16 ++++ .../aws/sqs/message/SqsIncomingMessage.java | 7 +- .../aws/sqs/message/SqsOutgoingMessage.java | 12 ++- .../message/SqsOutgoingMessageMetadata.java | 33 ++++++++ .../aws/sqs/SqsConnectorIncomingTest.java | 80 +++++++++++++++++++ .../aws/sqs/SqsConnectorOutgoingTest.java | 49 ++++++++++-- .../messaging/aws/sqs/base/SqsTestBase.java | 41 +++++----- .../messaging/aws/base/WeldTestBase.java | 4 + 22 files changed, 259 insertions(+), 84 deletions(-) create mode 100644 smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsMessages.java diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java index 71911de552..450793d99c 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Deserializer.java @@ -10,7 +10,7 @@ public interface Deserializer { * * @param payload message to deserialize * @return the deserialized message - * // TODO: ex + * TODO: ex * @throws RuntimeException in case the deserialization fails */ Object deserialize(String payload); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java index 689ceb6421..3faf0e132f 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/src/main/java/io/smallrye/reactive/messaging/aws/serialization/Serializer.java @@ -10,7 +10,7 @@ public interface Serializer { * * @param obj object to serialize * @return serialized message - * // TODO: ex + * TODO: ex * @throws RuntimeException in case serialization fails. */ String serialize(Object obj); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index faf782e7d7..35ba7ed18f 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -46,31 +46,31 @@ @Connector(SqsConnector.CONNECTOR_NAME) // common @ConnectorAttribute(name = "queue", type = "string", direction = INCOMING_AND_OUTGOING, description = "Set the SQS queue. If not set, the channel name is used") -@ConnectorAttribute(name = "health-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") -@ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "health-enabled", type = "boolean", direction = INCOMING_AND_OUTGOING, description = "Whether health reporting is enabled (default) or disabled", defaultValue = "true") +@ConnectorAttribute(name = "tracing-enabled", type = "boolean", direction = INCOMING_AND_OUTGOING, description = "Whether tracing is enabled (default) or disabled", defaultValue = "true") @ConnectorAttribute(name = "endpoint-override", type = "string", direction = INCOMING_AND_OUTGOING, description = "Configure the endpoint with which the SDK should communicate.") @ConnectorAttribute(name = "region", type = "string", direction = INCOMING_AND_OUTGOING, description = "Configure the region with which the SDK should communicate.") @ConnectorAttribute(name = "queue-resolver.queue-owner-aws-account-id", type = "string", direction = INCOMING_AND_OUTGOING, description = "During queue url resolving it is possible to overwrite the queue owner.") -@ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") +@ConnectorAttribute(name = "create-queue.enabled", type = "boolean", direction = INCOMING_AND_OUTGOING, description = "Whether automatic queue creation is enabled or disabled (default)", defaultValue = "false") // TODO: Not sure how to load maps. Maybe: key:value,key:value. It is not very efficient, but it is cached by the SqsTargetResolver. So maybe it does not matter. // Otherwise, I would need to wrap the config so that I can keep using the easy generated way, but also overwrite methods, to add config internal caching. @ConnectorAttribute(name = "create-queue.attributes", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of attributes for queue creation. Default empty.", defaultValue = "") @ConnectorAttribute(name = "create-queue.tags", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of tags for queue creation. Default empty.", defaultValue = "") -@ConnectorAttribute(name = "create-queue.dead-letter-queue.enabled", type = "boolean", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") -@ConnectorAttribute(name = "create-queue.dead-letter-queue.prefix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") -@ConnectorAttribute(name = "create-queue.dead-letter-queue.suffix", type = "string", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") -@ConnectorAttribute(name = "create-queue.dead-letter-queue.max-receive-count", type = "int", direction = ConnectorAttribute.Direction.INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.enabled", type = "boolean", direction = INCOMING_AND_OUTGOING, description = "Whether automatic dead-letter queue creation is enabled or disabled (default)", defaultValue = "false") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.prefix", type = "string", direction = INCOMING_AND_OUTGOING, description = "Dead-letter queue name prefix", defaultValue = "") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.suffix", type = "string", direction = INCOMING_AND_OUTGOING, description = "Dead-letter queue name suffix", defaultValue = "-dlq") +@ConnectorAttribute(name = "create-queue.dead-letter-queue.max-receive-count", type = "int", direction = INCOMING_AND_OUTGOING, description = "The number of times a message is delivered to the source queue before being moved to the dead-letter queue. Default: 10. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, Amazon SQS moves the message to the dead-letter-queue.", defaultValue = "10") @ConnectorAttribute(name = "create-queue.dead-letter-queue.attributes", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of attributes for queue creation. Default empty.", defaultValue = "") @ConnectorAttribute(name = "create-queue.dead-letter-queue.tags", type = "string", direction = INCOMING_AND_OUTGOING, description = "A comma separated list of tags for queue creation. Default empty.", defaultValue = "") -@ConnectorAttribute(name = "serialization-identifier", type = "string", direction = INCOMING_AND_OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") - // outgoing @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") +@ConnectorAttribute(name = "serialization-identifier", type = "string", direction = OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") + // incoming @ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") @ConnectorAttribute(name = "wait-time-seconds", type = "int", direction = INCOMING, description = "The duration (in seconds) for which the call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than WaitTimeSeconds. If no messages are available and the wait time expires, the call returns successfully with an empty list of messages. Default 20s.", defaultValue = "20") @@ -82,6 +82,8 @@ @ConnectorAttribute(name = "delete.batch.max-size", type = "int", direction = INCOMING, description = "The maximum number of messages to delete in a batch. Valid values: 1 to 10. Default: 10.", defaultValue = "10") @ConnectorAttribute(name = "delete.batch.max-delay", type = "int", direction = INCOMING, description = "The maximum number of seconds to wait for a batch. Needs to be configured lower than message visibility-timeout. Default: 3.", defaultValue = "3") +@ConnectorAttribute(name = "deserialization-identifier", type = "string", direction = INCOMING, description = "Name of the @Identifier to use. If not specified the channel name is used.") + public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { public static final String CONNECTOR_NAME = "smallrye-aws-sqs"; @@ -132,7 +134,7 @@ public Flow.Publisher> getPublisher(Config config) { SqsAsyncClient client = clients.computeIfAbsent(ic.getChannel(), ignored -> createSqsClient(ic, vertx)); final Deserializer deserializer = resolveDeserializer(messageDeserializer, ic - .getSerializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); + .getDeserializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); try { SqsIncomingChannel channel = new SqsIncomingChannel( diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java index 24ade3cbfb..7b4b2eb76b 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsIncomingChannel.java @@ -81,7 +81,7 @@ private Supplier> receiveMessagesSupplier() { private Multi> createMultiOfMessages(ReceiveMessageResponse response) { final Multi> result = Multi.createFrom().iterable(response.messages()) - .onItem().transform(msg -> SqsIncomingMessage.from(msg, ackHandler)); + .onItem().transform(msg -> SqsIncomingMessage.from(msg, ackHandler, clientHolder.getDeserializer())); return result.onItem().call(msg -> clientHolder.getTargetResolver().resolveTarget(clientHolder) .onItem().invoke(msg::withTarget)); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java index 33203cd255..3c8395eec7 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/ack/SqsAckHandler.java @@ -23,8 +23,7 @@ public SqsAckHandler(final SqsClientHolder cl if (Boolean.TRUE.equals(clientHolder.getConfig().getDeleteBatchEnabled())) { Multi.createFrom().> emitter(e -> emitter = e) - .group().intoLists().of(getMaxSize(clientHolder), Duration.ofSeconds(getMaxDelay(clientHolder)) - ) + .group().intoLists().of(getMaxSize(clientHolder), Duration.ofSeconds(getMaxDelay(clientHolder))) .onItem().call(messages -> DeleteMessageBatchAction.deleteMessages(clientHolder, messages)) .subscribe().with(ignored -> { }); @@ -38,8 +37,7 @@ private static Integer getMaxSize( private long getMaxDelay(final SqsClientHolder clientHolder) { return Math.max(1, Math.min( - clientHolder.getConfig().getDeleteBatchMaxDelay(), clientHolder.getConfig().getVisibilityTimeout() - 1) - ); + clientHolder.getConfig().getDeleteBatchMaxDelay(), clientHolder.getConfig().getVisibilityTimeout() - 1)); } public CompletionStage handle(final SqsIncomingMessage msg) { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java index 36d5406e3d..bcba83f8b8 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/CreateQueueAction.java @@ -16,7 +16,7 @@ /** * AWS + * "https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html">AWS * Documentation */ public class CreateQueueAction { @@ -24,13 +24,13 @@ public class CreateQueueAction { public static Uni createQueue(SqsClientHolder clientHolder, String queueName) { SqsConnectorCommonConfiguration config = clientHolder.getConfig(); - if (!config.getCreateQueueEnabled()) { + if (Boolean.FALSE.equals(config.getCreateQueueEnabled())) { return Uni.createFrom().nullItem(); } Uni> uni = Uni.createFrom().item(Map.of()); - if (config.getCreateQueueDeadLetterQueueEnabled()) { + if (Boolean.TRUE.equals(config.getCreateQueueDeadLetterQueueEnabled())) { // Parsing is slow. But we assume that creating queues does not happen that often. final Map attributes = parseToMap(config.getCreateQueueDeadLetterQueueAttributes()); final Map tags = parseToMap(config.getCreateQueueDeadLetterQueueTags()); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java index d38aedf6f3..627d733fb6 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageAction.java @@ -6,6 +6,11 @@ import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessage; import software.amazon.awssdk.services.sqs.model.DeleteMessageRequest; +/** + * AWS + * Documentation + */ public class DeleteMessageAction { public static Uni deleteMessage( diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java index e7aa90150f..357246a40b 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/DeleteMessageBatchAction.java @@ -14,6 +14,11 @@ import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; +/** + * AWS + * Documentation + */ public class DeleteMessageBatchAction { public static Uni deleteMessages( diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java index 09f0e2bf71..73942a6f98 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageAction.java @@ -7,6 +7,7 @@ import io.smallrye.reactive.messaging.aws.sqs.SqsConnectorOutgoingConfiguration; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessageMetadata; import software.amazon.awssdk.services.sqs.model.SendMessageRequest; /** @@ -19,15 +20,13 @@ public static Uni sendMessage( String payload = clientHolder.getSerializer().serialize(message.getPayload()); - SendMessageRequest request = SendMessageRequest.builder() - .queueUrl(message.getTarget().getTargetUrl()) - .messageAttributes(null) - .messageGroupId(null) - .messageBody(payload) + SqsOutgoingMessageMetadata metadata = message.getMetadata(SqsOutgoingMessageMetadata.class) + .orElseThrow(() -> new IllegalStateException("MetaData are expected to be set.")); - .messageDeduplicationId(null) - .delaySeconds(0) - .messageSystemAttributesWithStrings(null) + SendMessageRequest request = metadata.getMessageAugmenter() + .apply(SendMessageRequest.builder() + .queueUrl(message.getTarget().getTargetUrl()) + .messageBody(payload)) .build(); // TODO: logging @@ -38,7 +37,7 @@ public static Uni sendMessage( return Uni.createFrom().completionStage(message.ack()); }); - // TODO: configurable retry + // TODO: configurable retry and disable sdk retry mechanism? if (true) { uni = uni.onFailure().retry() .withBackOff(Duration.ofMillis(10), Duration.ofMillis(100)) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java index 14d1bc8d1e..fccd1fbdae 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/action/SendMessageBatchAction.java @@ -15,6 +15,7 @@ import io.smallrye.reactive.messaging.aws.sqs.SqsTarget; import io.smallrye.reactive.messaging.aws.sqs.client.SqsClientHolder; import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessage; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessageMetadata; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; import software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse; @@ -62,16 +63,14 @@ private static SendMessageBatchRequest createRequest( String payload = clientHolder.getSerializer().serialize(msg.getPayload()); String id = UUID.randomUUID().toString(); - SendMessageBatchRequestEntry entry = SendMessageBatchRequestEntry.builder() - // in batching we need to generate the id of a message for every entry. - .id(id) - .messageAttributes(null) - .messageGroupId(null) - .messageBody(payload) + SqsOutgoingMessageMetadata metadata = msg.getMetadata(SqsOutgoingMessageMetadata.class) + .orElseThrow(() -> new IllegalStateException("MetaData are expected to be set.")); - .messageDeduplicationId(null) - .delaySeconds(0) - .messageSystemAttributesWithStrings(null) + SendMessageBatchRequestEntry entry = metadata.getMessageBatchAugmenter() + .apply(SendMessageBatchRequestEntry.builder() + // in batching we need to generate the id of a message for every entry. + .id(id) + .messageBody(payload)) .build(); messageMap.put(id, msg); entryMap.put(id, entry); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java index 918c5ded2c..f77c7c4c5e 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientFactory.java @@ -18,7 +18,7 @@ /** * Based on some logic of the quarkiverse lib for aws - * TODO: Not sure about all of this. Maybe just for qualified SqsAsyncClient beans? This would allow to use it with + * TODO: Not sure about all of this. Maybe just qualified SqsAsyncClient beans? This would allow to use it with * Quarkiverse extension. Maybe. Or manually created clients. Otherwise, I would be forced to forward all possible * configurations. But sometimes configs are not flexible enough. The credentials provider topic is already * providing a lot of different approaches to configure them. diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java index 7689fa0fbe..c5c5bc31a3 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/client/SqsClientHolder.java @@ -8,6 +8,7 @@ import io.vertx.mutiny.core.Vertx; import software.amazon.awssdk.services.sqs.SqsAsyncClient; +// TODO: Maybe vertx is not needed. Not sure yet. public class SqsClientHolder extends ClientHolder { private final SqsTargetResolver targetResolver; diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java index a239879296..694d1adee8 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsExceptions.java @@ -10,18 +10,13 @@ public interface SqsExceptions { SqsExceptions ex = Messages.getBundle(SqsExceptions.class); - @Message(id = 19100, value = "Unable to build Pulsar client") + // TODO: ids? + @Message(id = 19100, value = "Unable to build SQS client") IllegalStateException illegalStateUnableToBuildClient(@Cause Throwable t); - @Message(id = 19101, value = "Unable to build Pulsar consumer") + @Message(id = 19101, value = "Unable to build SQS consumer") IllegalStateException illegalStateUnableToBuildConsumer(@Cause Throwable t); - @Message(id = 19102, value = "Unable to build Pulsar producer") + @Message(id = 19102, value = "Unable to build SQS producer") IllegalStateException illegalStateUnableToBuildProducer(@Cause Throwable t); - - @Message(id = 19103, value = "Expecting downstream to consume without back-pressure") - IllegalStateException illegalStateConsumeWithoutBackPressure(); - - @Message(id = 19104, value = "Only one subscriber allowed") - IllegalStateException illegalStateOnlyOneSubscriber(); } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java index 3212c08526..9e66c7cc99 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsLogging.java @@ -12,6 +12,7 @@ public interface SqsLogging extends BasicLogger { SqsLogging log = Logger.getMessageLogger(SqsLogging.class, "io.smallrye.reactive.messaging.aws.sqs"); + // TODO: ids? @LogMessage(level = Logger.Level.WARN) @Message(id = 19002, value = "Unable to close Sqs client") void unableToCloseClient(@Cause Throwable t); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsMessages.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsMessages.java new file mode 100644 index 0000000000..37fef6548f --- /dev/null +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/i18n/SqsMessages.java @@ -0,0 +1,16 @@ +package io.smallrye.reactive.messaging.aws.sqs.i18n; + +import org.jboss.logging.Messages; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageBundle; + +@MessageBundle(projectCode = "SRMSG", length = 5) +public interface SqsMessages { + + SqsMessages msg = Messages.getBundle(SqsMessages.class); + + // TODO: ids? + @Message(id = 19201, value = "%s is required") + String isRequired(String fieldName); + +} diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java index 84ac429dfb..99d4e6319d 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsIncomingMessage.java @@ -3,6 +3,7 @@ import java.util.concurrent.CompletionStage; import java.util.function.Supplier; +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; import io.smallrye.reactive.messaging.aws.sqs.ack.SqsAckHandler; import software.amazon.awssdk.services.sqs.model.Message; @@ -25,9 +26,9 @@ public Supplier> getAck() { return () -> this.ackHandler.handle(this); } - public static SqsIncomingMessage from(Message msg, SqsAckHandler ackHandler) { - // TODO: deserialize? + public static SqsIncomingMessage from( + Message msg, SqsAckHandler ackHandler, final Deserializer deserializer) { final SqsIncomingMessageMetadata sqsMetaData = new SqsIncomingMessageMetadata(msg); - return new SqsIncomingMessage<>(msg.body(), ackHandler, sqsMetaData); + return new SqsIncomingMessage<>(deserializer.deserialize(msg.body()), ackHandler, sqsMetaData); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java index 0c0a78019d..0328145821 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessage.java @@ -11,7 +11,10 @@ public class SqsOutgoingMessage extends SqsMessage> ack; private final Function> nack; - public SqsOutgoingMessage(final T payload, final SqsOutgoingMessageMetadata metadata, Supplier> ack, + public SqsOutgoingMessage( + final T payload, + final SqsOutgoingMessageMetadata metadata, + Supplier> ack, Function> nack) { super(payload, metadata); this.ack = ack; @@ -29,8 +32,11 @@ public Function> getNack() { } public static SqsOutgoingMessage from(Message message) { - return new SqsOutgoingMessage<>(message.getPayload(), message.getMetadata(SqsOutgoingMessageMetadata.class) - .orElseGet(SqsOutgoingMessageMetadata::new), message.getAck(), message.getNack()); + return new SqsOutgoingMessage<>( + message.getPayload(), + message.getMetadata(SqsOutgoingMessageMetadata.class).orElseGet(SqsOutgoingMessageMetadata::new), + message.getAck(), + message.getNack()); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java index ba24269f63..a0ea096620 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/message/SqsOutgoingMessageMetadata.java @@ -1,5 +1,38 @@ package io.smallrye.reactive.messaging.aws.sqs.message; +import static io.smallrye.reactive.messaging.aws.sqs.i18n.SqsMessages.msg; + +import java.util.Objects; +import java.util.function.UnaryOperator; + +import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; + public class SqsOutgoingMessageMetadata extends SqsMessageMetadata { + private UnaryOperator messageAugmenter = UnaryOperator.identity(); + + private UnaryOperator messageBatchAugmenter = UnaryOperator.identity(); + + public UnaryOperator getMessageAugmenter() { + return messageAugmenter; + } + + public SqsOutgoingMessageMetadata withMessageAugmenter( + final UnaryOperator messageAugmenter) { + Objects.requireNonNull(messageAugmenter, msg.isRequired("messageAugmenter")); + this.messageAugmenter = messageAugmenter; + return this; + } + + public UnaryOperator getMessageBatchAugmenter() { + return messageBatchAugmenter; + } + + public SqsOutgoingMessageMetadata withMessageBatchAugmenter( + final UnaryOperator messageBatchAugmenter) { + Objects.requireNonNull(messageAugmenter, msg.isRequired("messageBatchAugmenter")); + this.messageBatchAugmenter = messageBatchAugmenter; + return this; + } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java index cd75b00f4c..d9ca420b1a 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java @@ -7,21 +7,34 @@ import java.util.concurrent.CopyOnWriteArrayList; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import org.eclipse.microprofile.reactive.messaging.Incoming; import org.eclipse.microprofile.reactive.messaging.Message; +import org.jboss.weld.environment.se.Weld; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +import org.testcontainers.containers.localstack.LocalStackContainer; +import io.smallrye.common.annotation.Identifier; import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.aws.base.TestMapping; +import io.smallrye.reactive.messaging.aws.serialization.Deserializer; import io.smallrye.reactive.messaging.aws.sqs.base.SqsTestBase; import io.smallrye.reactive.messaging.aws.sqs.message.SqsIncomingMessageMetadata; import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; +// TODO: Split tests? @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class SqsConnectorIncomingTest extends SqsTestBase { + @Override + protected void registerBeanClasses(final Weld weld) { + super.registerBeanClasses(weld); + weld.addBeanClass(CustomDeserializer.class); + } + @Test void should_receive_message_via_payload() { // given @@ -42,6 +55,33 @@ void should_receive_message_via_payload() { }); } + @Test + void should_receive_message_via_custom_object() { + // given + MapBasedConfig config = new MapBasedConfig() + .with("mp.messaging.incoming.custom.endpoint-override", + LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) + .with("mp.messaging.incoming.custom.region", LOCAL_STACK_CONTAINER.getRegion()) + .with("mp.messaging.incoming.custom.connector", SqsConnector.CONNECTOR_NAME) + .with("mp.messaging.incoming.custom.wait-time-seconds", 2) + .with("mp.messaging.incoming.custom.queue", "test") + .with("mp.messaging.incoming.custom.deserialization-identifier", "custom"); + + TestAppCustomObject app = runApplication(config, TestAppCustomObject.class); + + sendMessage("{\"name\":\"test\"}"); + + // when + await().untilAsserted(() -> { + List received = app.received(); + + // then + assertThat(received).hasSize(1); + assertThat(received.get(0).name).isEqualTo("test"); + verifyNoInvisibleMessages(); + }); + } + @Test void should_receive_message_via_message() { // given @@ -170,6 +210,46 @@ public List received() { } } + @ApplicationScoped + public static class TestAppCustomObject { + + public static class CustomObject { + String name; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + } + + List received = new CopyOnWriteArrayList<>(); + + @Incoming("custom") + void consume(CustomObject msg) { + received.add(msg); + } + + public List received() { + return received; + } + } + + @ApplicationScoped + @Identifier("custom") + public static class CustomDeserializer implements Deserializer { + + @Inject + TestMapping testMapping; + + @Override + public Object deserialize(final String payload) { + return testMapping.fromJson(payload, TestAppCustomObject.CustomObject.class); + } + } + @ApplicationScoped public static class TestAppMsg { diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java index 3e5ee518fd..3ac0dd1f2b 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorOutgoingTest.java @@ -2,12 +2,14 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.List; +import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.eclipse.microprofile.reactive.messaging.Channel; +import org.eclipse.microprofile.reactive.messaging.Message; +import org.eclipse.microprofile.reactive.messaging.Metadata; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -16,7 +18,9 @@ import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import io.smallrye.reactive.messaging.MutinyEmitter; import io.smallrye.reactive.messaging.aws.sqs.base.SqsTestBase; +import io.smallrye.reactive.messaging.aws.sqs.message.SqsOutgoingMessageMetadata; import io.smallrye.reactive.messaging.test.common.config.MapBasedConfig; +import software.amazon.awssdk.services.sqs.model.MessageAttributeValue; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class SqsConnectorOutgoingTest extends SqsTestBase { @@ -32,9 +36,9 @@ void should_send_message_without_batching() { app.sendMessage("test").subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); // then - List messages = receiveMessages(); + var messages = receiveMessages(); assertThat(messages).hasSize(1); - assertThat(messages.get(0)).isEqualTo("test"); + assertThat(messages.get(0).body()).isEqualTo("test"); } @Test @@ -45,19 +49,40 @@ void should_send_message_with_batching() { TestApp app = runApplication(config, TestApp.class); // when - app.sendMessage("test").subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); + app.sendMessage("test") + .subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); + + // then + var messages = receiveMessages(); + assertThat(messages).hasSize(1); + assertThat(messages.get(0).body()).isEqualTo("test"); + } + + @Test + void should_send_message_with_metadata() { + // given + MapBasedConfig config = getOutgoingConfig(); + + TestApp app = runApplication(config, TestApp.class); + + // when + app.sendMessage("test", new SqsOutgoingMessageMetadata() + .withMessageAugmenter(b -> b.messageAttributes(Map.of( + "One", MessageAttributeValue.builder().dataType("String").stringValue("Piece").build())))) + .subscribe().withSubscriber(UniAssertSubscriber.create()).awaitItem().assertCompleted(); // then - List messages = receiveMessages(); + var messages = receiveMessages(); assertThat(messages).hasSize(1); - assertThat(messages.get(0)).isEqualTo("test"); + assertThat(messages.get(0).body()).isEqualTo("test"); + assertThat(messages.get(0).messageAttributes()).hasSize(1); + assertThat(messages.get(0).messageAttributes().get("One")).isNotNull(); + assertThat(messages.get(0).messageAttributes().get("One").stringValue()).isEqualTo("Piece"); } @ApplicationScoped public static class TestApp { - //List received = new CopyOnWriteArrayList<>(); - @Inject @Channel(QUEUE_NAME) MutinyEmitter emitter; @@ -65,5 +90,13 @@ public static class TestApp { Uni sendMessage(String msg) { return emitter.send(msg); } + + Uni sendMessage(String msg, SqsOutgoingMessageMetadata metadata) { + if (metadata != null) { + return emitter.sendMessage(Message.of(msg, Metadata.of(metadata))); + } else { + return emitter.send(msg); + } + } } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java index 7fa39c799d..db1a731c14 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/base/SqsTestBase.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; import org.eclipse.microprofile.reactive.messaging.spi.ConnectorLiteral; import org.jboss.logging.Logger; @@ -115,9 +114,7 @@ protected MapBasedConfig getIncomingConfig() { LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.SQS)) .with("mp.messaging.incoming.test.region", LOCAL_STACK_CONTAINER.getRegion()) .with("mp.messaging.incoming.test.connector", SqsConnector.CONNECTOR_NAME) - .with("mp.messaging.incoming.test.wait-time-seconds", 2) - //.with("mp.messaging.incoming.test.visibility-timeout",2) - ; + .with("mp.messaging.incoming.test.wait-time-seconds", 2); } private CompletableFuture getQueueUrl(String queueName) { @@ -139,21 +136,26 @@ protected void sendMessage(String queueName, String message) { } } - protected List receiveMessages() { + protected List receiveMessages() { return receiveMessages(QUEUE_NAME); } - protected List receiveMessages(String queueName) { + protected List receiveMessages(String queueName) { try { final ReceiveMessageResponse response = getQueueUrl(queueName).thenCompose( - queueUrl -> CLIENT.receiveMessage(b -> b.queueUrl(queueUrl).waitTimeSeconds(10).build())) + queueUrl -> CLIENT.receiveMessage(b -> b + .queueUrl(queueUrl) + .messageAttributeNames("All") + .attributeNames(QueueAttributeName.ALL) + .waitTimeSeconds(10) + .build())) .get(); if (!response.hasMessages()) { return Collections.emptyList(); } - return response.messages().stream().map(Message::body).collect(Collectors.toList()); + return response.messages(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } @@ -161,12 +163,10 @@ protected List receiveMessages(String queueName) { protected String getQueueAttribute(String queueName, QueueAttributeName attributeName) { try { - return getQueueUrl(queueName).thenCompose(queueUrl -> - CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() - .queueUrl(queueUrl) - .attributeNames(attributeName) - .build()) - ).get().attributes().get(attributeName); + return getQueueUrl(queueName).thenCompose(queueUrl -> CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(attributeName) + .build())).get().attributes().get(attributeName); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } @@ -174,12 +174,10 @@ protected String getQueueAttribute(String queueName, QueueAttributeName attribut protected Map getQueueAttributes(String queueName, QueueAttributeName... attributeNames) { try { - return getQueueUrl(queueName).thenCompose(queueUrl -> - CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() - .queueUrl(queueUrl) - .attributeNames(attributeNames) - .build()) - ).get().attributes(); + return getQueueUrl(queueName).thenCompose(queueUrl -> CLIENT.getQueueAttributes(GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(attributeNames) + .build())).get().attributes(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } @@ -190,8 +188,7 @@ protected void verifyNoInvisibleMessages() { } protected void verifyInvisibleMessages(int number) { - String msgsNotVisibile = - getQueueAttribute(QUEUE_NAME, APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE); + String msgsNotVisibile = getQueueAttribute(QUEUE_NAME, APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE); assertThat(msgsNotVisibile).isEqualTo(String.valueOf(number)); } } diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java index 64941d58db..5568dec114 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/src/main/java/io/smallrye/reactive/messaging/aws/base/WeldTestBase.java @@ -87,6 +87,10 @@ public BeanManager getBeanManager() { return container.getBeanManager(); } + public void addBeans(Class... clazzes) { + weld.addBeanClasses(clazzes); + } + public T get(Class clazz) { return getBeanManager().createInstance().select(clazz).get(); } From b8de08c3a0f65c2e927e1c2666e5e86fdc45d6f5 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:49:50 +0100 Subject: [PATCH 13/14] Make (de)serialization configurable. This fixes the tests as well. Jackson is more forgiving and can serialize a string to a string, but jakarta crashes in this case. This broke the tests. --- .../messaging/aws/sqs/SqsConnector.java | 30 +++++++++++++++---- .../aws/sqs/SqsConnectorIncomingTest.java | 3 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java index 35ba7ed18f..be0628ad01 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/main/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnector.java @@ -69,7 +69,8 @@ // outgoing @ConnectorAttribute(name = "send.batch.enabled", type = "boolean", direction = OUTGOING, description = "Send messages in batches.", defaultValue = "false") -@ConnectorAttribute(name = "serialization-identifier", type = "string", direction = OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") +@ConnectorAttribute(name = "serialization.enabled", type = "boolean", direction = OUTGOING, description = "Enable serialization. Default: false", defaultValue = "false") +@ConnectorAttribute(name = "serialization.identifier", type = "string", direction = OUTGOING, description = "Name of the @Identifier to use. If not specified the channel name is used.") // incoming @ConnectorAttribute(name = "max-number-of-messages", type = "int", direction = INCOMING, description = "The maximum number of messages to return. Amazon SQS never returns more messages than this value (however, fewer messages might be returned). Valid values: 1 to 10. Default: 10.", defaultValue = "10") @@ -82,7 +83,8 @@ @ConnectorAttribute(name = "delete.batch.max-size", type = "int", direction = INCOMING, description = "The maximum number of messages to delete in a batch. Valid values: 1 to 10. Default: 10.", defaultValue = "10") @ConnectorAttribute(name = "delete.batch.max-delay", type = "int", direction = INCOMING, description = "The maximum number of seconds to wait for a batch. Needs to be configured lower than message visibility-timeout. Default: 3.", defaultValue = "3") -@ConnectorAttribute(name = "deserialization-identifier", type = "string", direction = INCOMING, description = "Name of the @Identifier to use. If not specified the channel name is used.") +@ConnectorAttribute(name = "deserialization.enabled", type = "boolean", direction = INCOMING, description = "Enable deserialization. Default: false", defaultValue = "false") +@ConnectorAttribute(name = "deserialization.identifier", type = "string", direction = INCOMING, description = "Name of the @Identifier to use. If not specified the channel name is used.") public class SqsConnector implements InboundConnector, OutboundConnector, HealthReporter { @@ -133,8 +135,7 @@ public Flow.Publisher> getPublisher(Config config) { SqsAsyncClient client = clients.computeIfAbsent(ic.getChannel(), ignored -> createSqsClient(ic, vertx)); - final Deserializer deserializer = resolveDeserializer(messageDeserializer, ic - .getDeserializationIdentifier().orElse(ic.getChannel()), ic.getChannel(), jsonMapping); + final Deserializer deserializer = getDeserializer(ic); try { SqsIncomingChannel channel = new SqsIncomingChannel( @@ -146,14 +147,22 @@ public Flow.Publisher> getPublisher(Config config) { } } + private Deserializer getDeserializer(final SqsConnectorIncomingConfiguration ic) { + if (Boolean.TRUE.equals(ic.getDeserializationEnabled())) { + return resolveDeserializer(messageDeserializer, ic.getDeserializationIdentifier().orElse(ic.getChannel()), + ic.getChannel(), jsonMapping); + } else { + return payload -> payload; + } + } + @Override public Flow.Subscriber> getSubscriber(Config config) { SqsConnectorOutgoingConfiguration oc = new SqsConnectorOutgoingConfiguration(config); SqsAsyncClient client = clients.computeIfAbsent(oc.getChannel(), ignored -> createSqsClient(oc, vertx)); - final Serializer serializer = resolveSerializer(messageSerializer, - oc.getSerializationIdentifier().orElse(oc.getChannel()), oc.getChannel(), jsonMapping); + final Serializer serializer = getSerializer(oc); try { SqsOutgoingChannel channel = new SqsOutgoingChannel( @@ -165,6 +174,15 @@ public Flow.Subscriber> getSubscriber(Config config) { } } + private Serializer getSerializer(final SqsConnectorOutgoingConfiguration oc) { + if (Boolean.TRUE.equals(oc.getSerializationEnabled())) { + return resolveSerializer(messageSerializer, oc.getSerializationIdentifier().orElse(oc.getChannel()), + oc.getChannel(), jsonMapping); + } else { + return String::valueOf; + } + } + public void terminate( @Observes(notifyObserver = Reception.IF_EXISTS) @Priority(50) @BeforeDestroyed(ApplicationScoped.class) Object event) { channels.forEach(SqsChannel::close); diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java index d9ca420b1a..61eb7015f3 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/src/test/java/io/smallrye/reactive/messaging/aws/sqs/SqsConnectorIncomingTest.java @@ -65,7 +65,8 @@ void should_receive_message_via_custom_object() { .with("mp.messaging.incoming.custom.connector", SqsConnector.CONNECTOR_NAME) .with("mp.messaging.incoming.custom.wait-time-seconds", 2) .with("mp.messaging.incoming.custom.queue", "test") - .with("mp.messaging.incoming.custom.deserialization-identifier", "custom"); + .with("mp.messaging.incoming.custom.deserialization.enabled", "true") + .with("mp.messaging.incoming.custom.deserialization.identifier", "custom"); TestAppCustomObject app = runApplication(config, TestAppCustomObject.class); From 5b1cf7ea1c265d8f763e3428867b954f9a102328 Mon Sep 17 00:00:00 2001 From: holomekc <30546982+holomekc@users.noreply.github.com> Date: Mon, 18 Dec 2023 08:48:27 +0100 Subject: [PATCH 14/14] Merge upstream and change version --- smallrye-reactive-messaging-aws/pom.xml | 2 +- .../smallrye-reactive-messaging-aws-core/pom.xml | 2 +- .../smallrye-reactive-messaging-aws-sqs/pom.xml | 2 +- .../smallrye-reactive-messaging-aws-test/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/smallrye-reactive-messaging-aws/pom.xml b/smallrye-reactive-messaging-aws/pom.xml index 3bf3a7e8e8..db8c0aeada 100644 --- a/smallrye-reactive-messaging-aws/pom.xml +++ b/smallrye-reactive-messaging-aws/pom.xml @@ -7,7 +7,7 @@ io.smallrye.reactive smallrye-reactive-messaging - 4.11.0-SNAPSHOT + 4.14.0-SNAPSHOT smallrye-reactive-messaging-aws diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml index db61529b5f..29642f53a1 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-core/pom.xml @@ -6,7 +6,7 @@ io.smallrye.reactive smallrye-reactive-messaging-aws - 4.11.0-SNAPSHOT + 4.14.0-SNAPSHOT smallrye-reactive-messaging-aws-core diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml index 1a4fc6d71c..4f0904cc91 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-sqs/pom.xml @@ -6,7 +6,7 @@ io.smallrye.reactive smallrye-reactive-messaging-aws - 4.11.0-SNAPSHOT + 4.14.0-SNAPSHOT smallrye-reactive-messaging-aws-sqs diff --git a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml index 3716b69ee0..1198d82065 100644 --- a/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml +++ b/smallrye-reactive-messaging-aws/smallrye-reactive-messaging-aws-test/pom.xml @@ -6,7 +6,7 @@ io.smallrye.reactive smallrye-reactive-messaging-aws - 4.11.0-SNAPSHOT + 4.14.0-SNAPSHOT smallrye-reactive-messaging-aws-test