diff --git a/README.md b/README.md index dade2a2a..90f42ca5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The following provides some examples using the library with different languages ``` 1. In one of your beans, attach a - [@QueueListener](./spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java) + [@QueueListener](./annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java) annotation to a method indicating that it should process messages from a queue. ```java @@ -572,7 +572,7 @@ lambdaProcessor { The [Spring Cloud AWS Messaging](https://github.com/spring-cloud/spring-cloud-aws/tree/master/spring-cloud-aws-messaging) `@SqsListener` works by requesting a set of messages from the SQS and when they are done it will request some more. There is one disadvantage with this approach in that if 9/10 of the messages finish in 10 milliseconds but one takes 10 seconds no other messages will be picked up until that last message is complete. The -[@QueueListener](./spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java) +[@QueueListener](./annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java) provides the same basic functionality, but it also provides a timeout where it will eventually request for more messages when there are threads that are ready for another message. @@ -619,7 +619,7 @@ not prefetch anymore._ #### Spring Boot -The [@PrefetchingQueueListener](./spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java) +The [@PrefetchingQueueListener](./annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java) annotation can be used to prefetch messages in a background thread while processing the existing messages. The usage is something like this: ```java diff --git a/annotations/README.md b/annotations/README.md new file mode 100644 index 00000000..bd7a4be7 --- /dev/null +++ b/annotations/README.md @@ -0,0 +1,10 @@ +# Java Dynamic SQS Listener Annotations + +Wrapper around the core library that allows for setting up using annotations to simplify the usage. +ore message listener. + +## More Information + +For more information you can look at the root project [README.md](../README.md) which provides more information about the architecture +of the application. The [API](../api) is also a good location to find more information about what each part of the framework is how +they interact with each other. diff --git a/annotations/build.gradle.kts b/annotations/build.gradle.kts new file mode 100644 index 00000000..138588e7 --- /dev/null +++ b/annotations/build.gradle.kts @@ -0,0 +1,11 @@ + +description = "Contains a way to attach message listeners via annotations" + +dependencies { + api(project(":java-dynamic-sqs-listener-core")) + implementation(project(":common-utils")) + implementation(project(":annotation-utils")) + compileOnly(project(":documentation-annotations")) + + testImplementation(project(":elasticmq-sqs-client")) +} diff --git a/annotations/src/main/java/com/jashmore/sqs/annotations/container/AnnotationMessageListenerContainerFactory.java b/annotations/src/main/java/com/jashmore/sqs/annotations/container/AnnotationMessageListenerContainerFactory.java new file mode 100644 index 00000000..78ef1504 --- /dev/null +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/container/AnnotationMessageListenerContainerFactory.java @@ -0,0 +1,131 @@ +package com.jashmore.sqs.annotations.container; + +import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.argument.ArgumentResolverService; +import com.jashmore.sqs.client.QueueResolver; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainer; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.processor.CoreMessageProcessor; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.processor.MessageProcessor; +import com.jashmore.sqs.util.annotation.AnnotationUtils; +import com.jashmore.sqs.util.identifier.IdentifierUtils; +import com.jashmore.sqs.util.string.StringUtils; +import lombok.Builder; +import org.immutables.value.Value; +import software.amazon.awssdk.services.sqs.SqsAsyncClient; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link MessageListenerContainerFactory} that can be used to build against an annotated method + * @param + */ +public class AnnotationMessageListenerContainerFactory implements MessageListenerContainerFactory { + private final Class annotationClass; + private final Function identifierMapper; + private final Function sqsClientIdentifier; + private final Function queueNameOrUrlMapper; + private final QueueResolver queueResolver; + private final SqsAsyncClientProvider sqsAsyncClientProvider; + private final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory; + private final ArgumentResolverService argumentResolverService; + private final Function, MessageListenerContainer> containerFactory; + + /** + * Constructor. + * + * @param annotationClass the class instance of the annotation + * @param identifierMapper to convert an annotation to the identifier of the listener + * @param sqsClientIdentifierMapper to convert an annotation to the SQS Client identifier + * @param queueNameOrUrlMapper to convert an annotation to the Queue URL or name + * @param queueResolver to resolve queue names to a URL + * @param sqsAsyncClientProvider the method for obtaining a SQS client from the identifier + * @param decoratingMessageProcessorFactory to wrap the message processing with any decorators + * @param argumentResolverService to map the parameters of the method to values in the message + * @param containerFactory converts details about the annotation to the final {@link MessageListenerContainer} + */ + public AnnotationMessageListenerContainerFactory(final Class annotationClass, + final Function identifierMapper, + final Function sqsClientIdentifierMapper, + final Function queueNameOrUrlMapper, + final QueueResolver queueResolver, + final SqsAsyncClientProvider sqsAsyncClientProvider, + final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory, + final ArgumentResolverService argumentResolverService, + final Function, MessageListenerContainer> containerFactory) { + this.annotationClass = annotationClass; + this.identifierMapper = identifierMapper; + this.sqsClientIdentifier = sqsClientIdentifierMapper; + this.queueNameOrUrlMapper = queueNameOrUrlMapper; + this.queueResolver = queueResolver; + this.sqsAsyncClientProvider = sqsAsyncClientProvider; + this.decoratingMessageProcessorFactory = decoratingMessageProcessorFactory; + this.argumentResolverService = argumentResolverService; + this.containerFactory = containerFactory; + } + + @Override + public Optional buildContainer(final Object bean, final Method method) throws MessageListenerContainerInitialisationException { + return AnnotationUtils + .findMethodAnnotation(method, this.annotationClass) + .map(annotation -> { + final SqsAsyncClient sqsAsyncClient = getSqsAsyncClient(annotation); + final QueueProperties queueProperties = QueueProperties.builder() + .queueUrl(queueResolver.resolveQueueUrl(sqsAsyncClient, queueNameOrUrlMapper.apply(annotation))) + .build(); + final String identifier = IdentifierUtils.buildIdentifierForMethod(identifierMapper.apply(annotation), bean.getClass(), method); + + final Supplier messageProcessorSupplier = () -> + decoratingMessageProcessorFactory.decorateMessageProcessor( + sqsAsyncClient, + identifier, + queueProperties, + bean, + method, + new CoreMessageProcessor(argumentResolverService, queueProperties, sqsAsyncClient, method, bean) + ); + + return containerFactory.apply(AnnotationDetails.builder() + .identifier(identifier) + .queueProperties(queueProperties) + .sqsAsyncClient(sqsAsyncClient) + .messageProcessorSupplier(messageProcessorSupplier) + .annotation(annotation) + .build()); + }); + } + + private SqsAsyncClient getSqsAsyncClient(final A annotation) { + final String sqsClient = sqsClientIdentifier.apply(annotation); + + if (!StringUtils.hasText(sqsClient)) { + return sqsAsyncClientProvider + .getDefaultClient() + .orElseThrow(() -> new MessageListenerContainerInitialisationException("Expected the default SQS Client but there is none") + ); + } + + return sqsAsyncClientProvider + .getClient(sqsClient) + .orElseThrow(() -> + new MessageListenerContainerInitialisationException("Expected a client with id '" + sqsClient + "' but none were found") + ); + } + + @Value + @Builder + public static class AnnotationDetails { + public String identifier; + public SqsAsyncClient sqsAsyncClient; + public QueueProperties queueProperties; + public Supplier messageProcessorSupplier; + public A annotation; + } +} diff --git a/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/BasicAnnotationMessageListenerContainerFactory.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/BasicAnnotationMessageListenerContainerFactory.java new file mode 100644 index 00000000..f5e0e14e --- /dev/null +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/BasicAnnotationMessageListenerContainerFactory.java @@ -0,0 +1,58 @@ +package com.jashmore.sqs.annotations.core.basic; + +import com.jashmore.sqs.annotations.container.AnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.argument.ArgumentResolverService; +import com.jashmore.sqs.client.QueueResolver; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainer; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.container.batching.BatchingMessageListenerContainer; +import com.jashmore.sqs.container.batching.BatchingMessageListenerContainerProperties; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; + +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * {@link MessageListenerContainerFactory} that will wrap methods annotated with + * {@link QueueListener @QueueListener} with some predefined implementations of the framework. + */ +public class BasicAnnotationMessageListenerContainerFactory implements MessageListenerContainerFactory { + + private final AnnotationMessageListenerContainerFactory delegate; + + public BasicAnnotationMessageListenerContainerFactory( + final ArgumentResolverService argumentResolverService, + final SqsAsyncClientProvider sqsAsyncClientProvider, + final QueueResolver queueResolver, + final QueueListenerParser queueListenerParser, + final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory + ) { + this.delegate = new AnnotationMessageListenerContainerFactory<>( + QueueListener.class, + QueueListener::identifier, + QueueListener::sqsClient, + QueueListener::value, + queueResolver, + sqsAsyncClientProvider, + decoratingMessageProcessorFactory, + argumentResolverService, + (details) -> { + final BatchingMessageListenerContainerProperties properties = queueListenerParser.parse(details.annotation); + return new BatchingMessageListenerContainer( + details.identifier, + details.queueProperties, + details.sqsAsyncClient, + details.messageProcessorSupplier, + properties + ); + } + ); + } + + @Override + public Optional buildContainer(final Object bean, final Method method) throws MessageListenerContainerInitialisationException { + return this.delegate.buildContainer(bean, method); + } +} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java similarity index 95% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java index 9d85649e..fa8c05fb 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java @@ -1,9 +1,10 @@ -package com.jashmore.sqs.spring.container.basic; +package com.jashmore.sqs.annotations.core.basic; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.aws.AwsConstants; import com.jashmore.sqs.broker.concurrent.ConcurrentMessageBroker; import com.jashmore.sqs.broker.concurrent.ConcurrentMessageBrokerProperties; @@ -11,19 +12,18 @@ import com.jashmore.sqs.processor.CoreMessageProcessor; import com.jashmore.sqs.retriever.batching.BatchingMessageRetriever; import com.jashmore.sqs.retriever.batching.BatchingMessageRetrieverProperties; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** * Wrap a method with a {@link MessageListenerContainer} that will execute the method whenever a message is received on the provided queue. * *

This is a simplified annotation that uses the {@link ConcurrentMessageBroker}, {@link BatchingMessageRetriever} and {@link CoreMessageProcessor} - * for the implementations of the framework. Not all of the properties for each implementation are available to simplify this usage. + * for the implementations of the framework. Not all the properties for each implementation are available to simplify this usage. * - * @see BasicMessageListenerContainerFactory for what processes this annotation + * @see BasicAnnotationMessageListenerContainerFactory for what processes this annotation */ @Retention(RUNTIME) @Target(METHOD) @@ -40,7 +40,7 @@ * * * @return the queue name or URL of the queue - * @see Environment#resolveRequiredPlaceholders(String) for how the placeholders are resolved + * @see PlaceholderResolver#resolvePlaceholders(String) for how the placeholders are resolved * @see QueueProperties#getQueueUrl() for how the URL of the queue is resolved if a queue name is supplied here */ String value(); diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListenerParser.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParser.java similarity index 90% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListenerParser.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParser.java index 7091c244..1e978cf1 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListenerParser.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParser.java @@ -1,29 +1,28 @@ -package com.jashmore.sqs.spring.container.basic; +package com.jashmore.sqs.annotations.core.basic; import com.jashmore.documentation.annotations.Max; import com.jashmore.documentation.annotations.Nullable; import com.jashmore.documentation.annotations.Positive; import com.jashmore.documentation.annotations.PositiveOrZero; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.aws.AwsConstants; import com.jashmore.sqs.container.batching.BatchingMessageListenerContainerProperties; -import com.jashmore.sqs.spring.container.CoreAnnotationParser; +import com.jashmore.sqs.util.string.StringUtils; + import java.time.Duration; import java.util.function.Supplier; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; /** * Parser that is used to transform a {@link QueueListener} annotation to a {@link BatchingMessageListenerContainerProperties}. */ -public class QueueListenerParser implements CoreAnnotationParser { +public class QueueListenerParser { - private final Environment environment; + private final PlaceholderResolver placeholderResolver; - public QueueListenerParser(final Environment environment) { - this.environment = environment; + public QueueListenerParser(final PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; } - @Override public BatchingMessageListenerContainerProperties parse(QueueListener annotation) { final Supplier concurrencySupplier = concurrencySupplier(annotation); final Supplier concurrencyPollingRateSupplier = concurrencyPollingRateSupplier(annotation); @@ -102,7 +101,7 @@ protected Supplier concurrencySupplier(final QueueListener annotation) if (!StringUtils.hasText(annotation.concurrencyLevelString())) { concurrencyLevel = annotation.concurrencyLevel(); } else { - concurrencyLevel = Integer.parseInt(environment.resolvePlaceholders(annotation.concurrencyLevelString())); + concurrencyLevel = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.concurrencyLevelString())); } return () -> concurrencyLevel; } @@ -134,7 +133,7 @@ protected Supplier batchSizeSupplier(final QueueListener annotation) { if (!StringUtils.hasText(annotation.batchSizeString())) { batchSize = annotation.batchSize(); } else { - batchSize = Integer.parseInt(environment.resolvePlaceholders(annotation.batchSizeString())); + batchSize = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.batchSizeString())); } return () -> batchSize; @@ -154,7 +153,7 @@ protected Supplier batchingPeriodSupplier(final QueueListener annotati if (!StringUtils.hasText(annotation.batchingPeriodInMsString())) { batchingPeriod = Duration.ofMillis(annotation.batchingPeriodInMs()); } else { - batchingPeriod = Duration.ofMillis(Integer.parseInt(environment.resolvePlaceholders(annotation.batchingPeriodInMsString()))); + batchingPeriod = Duration.ofMillis(Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.batchingPeriodInMsString()))); } return () -> batchingPeriod; } @@ -193,7 +192,7 @@ protected Supplier messageVisibilityTimeoutSupplier(final QueueListene } } else { messageVisibilityTimeout = - Duration.ofSeconds(Integer.parseInt(environment.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); + Duration.ofSeconds(Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); } return () -> messageVisibilityTimeout; diff --git a/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoAnnotationMessageListenerContainerFactory.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoAnnotationMessageListenerContainerFactory.java new file mode 100644 index 00000000..55d290fa --- /dev/null +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoAnnotationMessageListenerContainerFactory.java @@ -0,0 +1,65 @@ +package com.jashmore.sqs.annotations.core.fifo; + +import com.jashmore.sqs.annotations.container.AnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.argument.ArgumentResolverService; +import com.jashmore.sqs.container.MessageListenerContainer; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.container.fifo.FifoMessageListenerContainer; +import com.jashmore.sqs.container.fifo.FifoMessageListenerContainerProperties; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.client.QueueResolver; + +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * {@link MessageListenerContainerFactory} that will wrap methods annotated with {@link FifoQueueListener @FifoQueueListener} with + * a {@link FifoMessageListenerContainer} that will automatically handle processing of messages coming from a FIFO SQS Queue. + * + *

A Spring bean needs to have a method annotated with this annotation like: + * + *

+ *     @FifoQueueListener(value = "test-queue.fifo", concurrencyLevel = 10, maximumMessagesInMessageGroup = 2)
+ *     public void myMessageProcessor(Message message) {
+ * 
+ */ +public class FifoAnnotationMessageListenerContainerFactory implements MessageListenerContainerFactory { + + private final AnnotationMessageListenerContainerFactory delegate; + + public FifoAnnotationMessageListenerContainerFactory( + final ArgumentResolverService argumentResolverService, + final SqsAsyncClientProvider sqsAsyncClientProvider, + final QueueResolver queueResolver, + final FifoQueueListenerParser annotationParser, + final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory + ) { + this.delegate = new AnnotationMessageListenerContainerFactory<>( + FifoQueueListener.class, + FifoQueueListener::identifier, + FifoQueueListener::sqsClient, + FifoQueueListener::value, + queueResolver, + sqsAsyncClientProvider, + decoratingMessageProcessorFactory, + argumentResolverService, + (details) -> { + final FifoMessageListenerContainerProperties properties = annotationParser.parse(details.annotation); + return new FifoMessageListenerContainer( + details.identifier, + details.queueProperties, + details.sqsAsyncClient, + details.messageProcessorSupplier, + properties + ); + } + ); + } + + @Override + public Optional buildContainer(Object bean, Method method) throws MessageListenerContainerInitialisationException { + return this.delegate.buildContainer(bean, method); + } +} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListener.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListener.java similarity index 96% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListener.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListener.java index dee81151..97789edb 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListener.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListener.java @@ -1,25 +1,25 @@ -package com.jashmore.sqs.spring.container.fifo; +package com.jashmore.sqs.annotations.core.fifo; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.aws.AwsConstants; import com.jashmore.sqs.broker.grouping.GroupingMessageBroker; import com.jashmore.sqs.broker.grouping.GroupingMessageBrokerProperties; import com.jashmore.sqs.container.MessageListenerContainer; import com.jashmore.sqs.container.fifo.FifoMessageListenerContainerProperties; import com.jashmore.sqs.retriever.batching.BatchingMessageRetrieverProperties; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** * Wrap a method with a {@link MessageListenerContainer} that will listen to a FIFO SQS queue. * - * @see FifoMessageListenerContainerFactory for what processes this annotation + * @see FifoAnnotationMessageListenerContainerFactory for what processes this annotation */ @Retention(RUNTIME) @Target(METHOD) @@ -38,7 +38,7 @@ * * * @return the queue name or URL of the queue - * @see Environment#resolveRequiredPlaceholders(String) for how the placeholders are resolved + * @see PlaceholderResolver#resolvePlaceholders(String) for how the placeholders are resolved * @see QueueProperties#getQueueUrl() for how the URL of the queue is resolved if a queue name is supplied here */ String value(); diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParser.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParser.java similarity index 89% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParser.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParser.java index c97af15c..119e42e9 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParser.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParser.java @@ -1,25 +1,24 @@ -package com.jashmore.sqs.spring.container.fifo; +package com.jashmore.sqs.annotations.core.fifo; import com.jashmore.documentation.annotations.Nullable; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.container.fifo.FifoMessageListenerContainerProperties; -import com.jashmore.sqs.spring.container.CoreAnnotationParser; +import com.jashmore.sqs.util.string.StringUtils; + import java.time.Duration; import java.util.function.Supplier; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; /** * Parser that is used to transform a {@link FifoQueueListener} annotation to a {@link FifoMessageListenerContainerProperties}. */ -public class FifoQueueListenerParser implements CoreAnnotationParser { +public class FifoQueueListenerParser { - private final Environment environment; + private final PlaceholderResolver placeholderResolver; - public FifoQueueListenerParser(final Environment environment) { - this.environment = environment; + public FifoQueueListenerParser(final PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; } - @Override public FifoMessageListenerContainerProperties parse(final FifoQueueListener annotation) { final Supplier concurrencySupplier = concurrencySupplier(annotation); final Supplier concurrencyPollingRateSupplier = concurrencyPollingRateSupplier(annotation); @@ -89,7 +88,7 @@ protected Supplier concurrencySupplier(final FifoQueueListener annotati if (!StringUtils.hasText(annotation.concurrencyLevelString())) { concurrencyLevel = annotation.concurrencyLevel(); } else { - concurrencyLevel = Integer.parseInt(environment.resolvePlaceholders(annotation.concurrencyLevelString())); + concurrencyLevel = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.concurrencyLevelString())); } return () -> concurrencyLevel; } @@ -121,7 +120,7 @@ protected Supplier maximumMessagesInGroupSupplier(final FifoQueueListen if (!StringUtils.hasText(annotation.maximumMessagesInMessageGroupString())) { batchSize = annotation.maximumMessagesInMessageGroup(); } else { - batchSize = Integer.parseInt(environment.resolvePlaceholders(annotation.maximumMessagesInMessageGroupString())); + batchSize = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.maximumMessagesInMessageGroupString())); } return () -> batchSize; @@ -141,7 +140,7 @@ protected Supplier maximumCachedMessageGroupsSupplier(final FifoQueueLi if (!StringUtils.hasText(annotation.maximumCachedMessageGroupsString())) { maximumCachedMessageGroups = annotation.maximumCachedMessageGroups(); } else { - maximumCachedMessageGroups = Integer.parseInt(environment.resolvePlaceholders(annotation.maximumCachedMessageGroupsString())); + maximumCachedMessageGroups = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.maximumCachedMessageGroupsString())); } return () -> maximumCachedMessageGroups; } @@ -179,7 +178,7 @@ protected Supplier messageVisibilityTimeoutSupplier(final FifoQueueLis } } else { messageVisibilityTimeout = - Duration.ofSeconds(Integer.parseInt(environment.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); + Duration.ofSeconds(Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); } return () -> messageVisibilityTimeout; diff --git a/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingAnnotationMessageListenerContainerFactory.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingAnnotationMessageListenerContainerFactory.java new file mode 100644 index 00000000..bb51a13c --- /dev/null +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingAnnotationMessageListenerContainerFactory.java @@ -0,0 +1,58 @@ +package com.jashmore.sqs.annotations.core.prefetch; + +import com.jashmore.sqs.annotations.container.AnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.argument.ArgumentResolverService; +import com.jashmore.sqs.container.MessageListenerContainer; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainer; +import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainerProperties; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.client.QueueResolver; + +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * {@link MessageListenerContainerFactory} that will wrap methods annotated with {@link PrefetchingQueueListener @PrefetchingQueueListener} with + * some predefined implementations of the framework. + */ +public class PrefetchingAnnotationMessageListenerContainerFactory implements MessageListenerContainerFactory { + + private final AnnotationMessageListenerContainerFactory delegate; + + public PrefetchingAnnotationMessageListenerContainerFactory( + final ArgumentResolverService argumentResolverService, + final SqsAsyncClientProvider sqsAsyncClientProvider, + final QueueResolver queueResolver, + final PrefetchingQueueListenerParser annotationParser, + final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory + ) { + this.delegate = new AnnotationMessageListenerContainerFactory<>( + PrefetchingQueueListener.class, + PrefetchingQueueListener::identifier, + PrefetchingQueueListener::sqsClient, + PrefetchingQueueListener::value, + queueResolver, + sqsAsyncClientProvider, + decoratingMessageProcessorFactory, + argumentResolverService, + (details) -> { + final PrefetchingMessageListenerContainerProperties properties = annotationParser.parse(details.annotation); + return new PrefetchingMessageListenerContainer( + details.identifier, + details.queueProperties, + details.sqsAsyncClient, + details.messageProcessorSupplier, + properties + ); + } + ); + } + + @Override + public Optional buildContainer(Object bean, Method method) throws MessageListenerContainerInitialisationException { + return this.delegate.buildContainer(bean, method); + } +} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java similarity index 94% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java index 51f9a479..706a6493 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java @@ -1,10 +1,11 @@ -package com.jashmore.sqs.spring.container.prefetch; +package com.jashmore.sqs.annotations.core.prefetch; import static com.jashmore.sqs.aws.AwsConstants.MAX_NUMBER_OF_MESSAGES_FROM_SQS; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.broker.concurrent.ConcurrentMessageBroker; import com.jashmore.sqs.broker.concurrent.ConcurrentMessageBrokerProperties; import com.jashmore.sqs.container.MessageListenerContainer; @@ -12,19 +13,18 @@ import com.jashmore.sqs.retriever.batching.BatchingMessageRetrieverProperties; import com.jashmore.sqs.retriever.prefetch.PrefetchingMessageRetriever; import com.jashmore.sqs.retriever.prefetch.PrefetchingMessageRetrieverProperties; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** * Wrap a method with a {@link MessageListenerContainer} that will execute the method whenever a message is received on the provided queue. * *

This is a simplified annotation that uses the {@link ConcurrentMessageBroker}, {@link PrefetchingMessageRetriever} and {@link CoreMessageProcessor} - * for the implementations of the framework. Not all of the properties for each implementation are available to simplify this usage. + * for the implementations of the framework. Not all the properties for each implementation are available to simplify this usage. * - * @see PrefetchingMessageListenerContainerFactory for what processes this annotation + * @see PrefetchingAnnotationMessageListenerContainerFactory for what processes this annotation */ @Retention(RUNTIME) @Target(METHOD) @@ -41,7 +41,7 @@ * * * @return the queue name or URL of the queue - * @see Environment#resolveRequiredPlaceholders(String) for how the placeholders are resolved + * @see PlaceholderResolver#resolvePlaceholders(String) for how the placeholders are resolved * @see QueueProperties#getQueueUrl() for how the URL of the queue is resolved if a queue name is supplied here */ String value(); diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParser.java b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParser.java similarity index 89% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParser.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParser.java index 3886bca0..a5ba1c94 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParser.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParser.java @@ -1,28 +1,26 @@ -package com.jashmore.sqs.spring.container.prefetch; +package com.jashmore.sqs.annotations.core.prefetch; import com.jashmore.documentation.annotations.Nullable; import com.jashmore.documentation.annotations.Positive; import com.jashmore.documentation.annotations.PositiveOrZero; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainerProperties; -import com.jashmore.sqs.spring.container.CoreAnnotationParser; +import com.jashmore.sqs.util.string.StringUtils; + import java.time.Duration; import java.util.function.Supplier; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; /** * Parser that is used to transform a {@link PrefetchingQueueListener} annotation to a {@link PrefetchingMessageListenerContainerProperties}. */ -public class PrefetchingQueueListenerParser - implements CoreAnnotationParser { +public class PrefetchingQueueListenerParser { - private final Environment environment; + private final PlaceholderResolver placeholderResolver; - public PrefetchingQueueListenerParser(final Environment environment) { - this.environment = environment; + public PrefetchingQueueListenerParser(final PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; } - @Override public PrefetchingMessageListenerContainerProperties parse(final PrefetchingQueueListener annotation) { final Supplier concurrencySupplier = concurrencySupplier(annotation); final Supplier concurrencyPollingRateSupplier = concurrencyPollingRateSupplier(annotation); @@ -100,7 +98,7 @@ protected Supplier concurrencySupplier(final PrefetchingQueueListener a if (!StringUtils.hasText(annotation.concurrencyLevelString())) { concurrencyLevel = annotation.concurrencyLevel(); } else { - concurrencyLevel = Integer.parseInt(environment.resolvePlaceholders(annotation.concurrencyLevelString())); + concurrencyLevel = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.concurrencyLevelString())); } return () -> concurrencyLevel; } @@ -133,7 +131,7 @@ protected Supplier desiredMinPrefetchedMessagesSupplier(final Prefetchi desiredMinPrefetchedMessages = annotation.desiredMinPrefetchedMessages(); } else { desiredMinPrefetchedMessages = - Integer.parseInt(environment.resolvePlaceholders(annotation.desiredMinPrefetchedMessagesString())); + Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.desiredMinPrefetchedMessagesString())); } return () -> desiredMinPrefetchedMessages; } @@ -152,7 +150,7 @@ protected Supplier maxPrefetchedMessagesSupplier(final PrefetchingQueue if (!StringUtils.hasText(annotation.maxPrefetchedMessagesString())) { maxPrefetchedMessages = annotation.maxPrefetchedMessages(); } else { - maxPrefetchedMessages = Integer.parseInt(environment.resolvePlaceholders(annotation.maxPrefetchedMessagesString())); + maxPrefetchedMessages = Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.maxPrefetchedMessagesString())); } return () -> maxPrefetchedMessages; } @@ -191,7 +189,7 @@ protected Supplier messageVisibilityTimeoutSupplier(final PrefetchingQ } } else { messageVisibilityTimeout = - Duration.ofSeconds(Integer.parseInt(environment.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); + Duration.ofSeconds(Integer.parseInt(placeholderResolver.resolvePlaceholders(annotation.messageVisibilityTimeoutInSecondsString()))); } return () -> messageVisibilityTimeout; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtender.java b/annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtender.java similarity index 98% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtender.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtender.java index 38e18562..dd33a4f1 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtender.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtender.java @@ -1,10 +1,11 @@ -package com.jashmore.sqs.spring.decorator.visibilityextender; +package com.jashmore.sqs.annotations.decorator.visibilityextender; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecorator; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecoratorProperties; + import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java b/annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java similarity index 87% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java rename to annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java index cbfefbf9..f87009b5 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java +++ b/annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactory.java @@ -1,19 +1,20 @@ -package com.jashmore.sqs.spring.decorator.visibilityextender; +package com.jashmore.sqs.annotations.decorator.visibilityextender; import com.jashmore.documentation.annotations.VisibleForTesting; import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecorator; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecoratorProperties; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactory; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactoryException; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactory; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactoryException; import com.jashmore.sqs.util.annotation.AnnotationUtils; import java.lang.reflect.Method; import java.time.Duration; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; + +import com.jashmore.sqs.util.string.StringUtils; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** @@ -23,10 +24,10 @@ public class AutoVisibilityExtenderMessageProcessingDecoratorFactory implements MessageProcessingDecoratorFactory { - private final Environment environment; + private final PlaceholderResolver placeholderResolver; - public AutoVisibilityExtenderMessageProcessingDecoratorFactory(final Environment environment) { - this.environment = environment; + public AutoVisibilityExtenderMessageProcessingDecoratorFactory(final PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; } @Override @@ -108,7 +109,7 @@ private Duration getDurationFromSeconds( ) { final String stringPropertyValue = stringProperty.get(); if (StringUtils.hasText(stringPropertyValue)) { - return Duration.ofSeconds(Integer.parseInt(environment.resolvePlaceholders(stringPropertyValue))); + return Duration.ofSeconds(Integer.parseInt(placeholderResolver.resolvePlaceholders(stringPropertyValue))); } final Integer integerValue = integerSupplier.get(); diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactoryTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/BasicMessageListenerContainerFactoryTest.java similarity index 77% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactoryTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/BasicMessageListenerContainerFactoryTest.java index d603fe84..c38ee094 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactoryTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/BasicMessageListenerContainerFactoryTest.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.container.basic; +package com.jashmore.sqs.annotations.core.basic; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -10,10 +10,11 @@ import com.jashmore.sqs.argument.ArgumentResolverService; import com.jashmore.sqs.container.MessageListenerContainer; import com.jashmore.sqs.container.batching.BatchingMessageListenerContainer; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.MessageListenerContainerInitialisationException; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.placeholder.PlaceholderResolver; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.client.QueueResolver; import java.lang.reflect.Method; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -21,7 +22,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** @@ -45,21 +45,21 @@ class BasicMessageListenerContainerFactoryTest { QueueResolver queueResolver; @Mock - Environment environment; + PlaceholderResolver placeholderResolver; @Mock DecoratingMessageProcessorFactory decoratingMessageProcessorFactory; - private BasicMessageListenerContainerFactory messageListenerContainerFactory; + private BasicAnnotationMessageListenerContainerFactory messageListenerContainerFactory; @BeforeEach void setUp() { messageListenerContainerFactory = - new BasicMessageListenerContainerFactory( + new BasicAnnotationMessageListenerContainerFactory( argumentResolverService, sqsAsyncClientProvider, queueResolver, - new QueueListenerParser(environment), + new QueueListenerParser(placeholderResolver), decoratingMessageProcessorFactory ); } @@ -72,11 +72,11 @@ void queueListenerWrapperCanBuildMessageListenerContainer() throws NoSuchMethodE final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("myMethod"); // act - final MessageListenerContainer messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer).isInstanceOf(BatchingMessageListenerContainer.class); + assertThat(messageListenerContainer).isNotEmpty(); + assertThat(messageListenerContainer).containsInstanceOf(BatchingMessageListenerContainer.class); } @Test @@ -87,11 +87,11 @@ void queueListenerWrapperWithoutIdentifierWillConstructOneByDefault() throws NoS final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("myMethod"); // act - final MessageListenerContainer messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer.getIdentifier()).isEqualTo("basic-message-listener-container-factory-test-my-method"); + assertThat(messageListenerContainer).isNotEmpty(); + assertThat(messageListenerContainer.get().getIdentifier()).isEqualTo("basic-message-listener-container-factory-test-my-method"); } @Test @@ -102,11 +102,11 @@ void queueListenerWrapperWithIdentifierWillUseThatForTheMessageListenerContainer final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("myMethodWithIdentifier"); // act - final MessageListenerContainer messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer.getIdentifier()).isEqualTo("identifier"); + assertThat(messageListenerContainer).isNotEmpty(); + assertThat(messageListenerContainer.get().getIdentifier()).isEqualTo("identifier"); } @Test @@ -127,8 +127,8 @@ void queueIsResolvedViaTheQueueResolver() throws NoSuchMethodException { void invalidConcurrencyLevelStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultSqsAsyncClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.concurrency}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.concurrency}")).thenReturn("Test Invalid"); final Object bean = new BasicMessageListenerContainerFactoryTest(); final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -140,8 +140,8 @@ void invalidConcurrencyLevelStringFailsToWrapMessageListener() throws Exception void invalidMessageVisibilityTimeoutInSecondsStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultSqsAsyncClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.visibility}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.visibility}")).thenReturn("Test Invalid"); final Object bean = new BasicMessageListenerContainerFactoryTest(); final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -153,8 +153,8 @@ void invalidMessageVisibilityTimeoutInSecondsStringFailsToWrapMessageListener() void invalidMaxPeriodBetweenBatchesInMsStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultSqsAsyncClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.period}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.period}")).thenReturn("Test Invalid"); final Object bean = new BasicMessageListenerContainerFactoryTest(); final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -166,15 +166,15 @@ void invalidMaxPeriodBetweenBatchesInMsStringFailsToWrapMessageListener() throws void validStringFieldsWillCorrectlyBuildMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultSqsAsyncClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); final Object bean = new BasicMessageListenerContainerFactoryTest(); final Method method = BasicMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); // act - final MessageListenerContainer messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = messageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); + assertThat(messageListenerContainer).isNotEmpty(); } @Test @@ -219,10 +219,10 @@ void whenSpecificSqsClientRequestWhichCanBeFoundTheContainerCanBeBuilt() throws when(sqsAsyncClientProvider.getClient("clientId")).thenReturn(Optional.of(mock(SqsAsyncClient.class))); // act - final MessageListenerContainer container = messageListenerContainerFactory.buildContainer(bean, method); + final Optional container = messageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(container).isNotNull(); + assertThat(container).isNotEmpty(); } @QueueListener("test") diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/QueueListenerParserTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParserTest.java similarity index 86% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/QueueListenerParserTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParserTest.java index 4c81bdd3..1b901019 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/basic/QueueListenerParserTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParserTest.java @@ -1,23 +1,27 @@ -package com.jashmore.sqs.spring.container.basic; +package com.jashmore.sqs.annotations.core.basic; import static org.assertj.core.api.Assertions.assertThat; import com.jashmore.sqs.container.batching.BatchingMessageListenerContainerProperties; import java.time.Duration; + +import com.jashmore.sqs.placeholder.StaticPlaceholderResolver; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.mock.env.MockEnvironment; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) class QueueListenerParserTest { - MockEnvironment mockEnvironment; + StaticPlaceholderResolver placeholderResolver = new StaticPlaceholderResolver(); QueueListenerParser parser; @BeforeEach void setUp() { - mockEnvironment = new MockEnvironment(); - parser = new QueueListenerParser(mockEnvironment); + placeholderResolver.reset(); + parser = new QueueListenerParser(placeholderResolver); } @Test @@ -74,11 +78,11 @@ void stringFieldsWithRawValues() throws Exception { @Test void stringFieldsWithReplacements() throws Exception { // arrange - mockEnvironment - .withProperty("queue.concurrencyLevel", "2") - .withProperty("queue.batchSize", "3") - .withProperty("queue.batchingPeriodInMs", "500") - .withProperty("queue.messageVisibilityInSeconds", "5"); + placeholderResolver + .withMapping("${queue.concurrencyLevel}", "2") + .withMapping("${queue.batchSize}", "3") + .withMapping("${queue.batchingPeriodInMs}", "500") + .withMapping("${queue.messageVisibilityInSeconds}", "5"); final QueueListener annotation = QueueListenerParserTest.class.getMethod("stringMethodWithReplacements").getAnnotation(QueueListener.class); diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParserTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParserTest.java similarity index 88% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParserTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParserTest.java index 06e1b1af..02255a10 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParserTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParserTest.java @@ -1,23 +1,24 @@ -package com.jashmore.sqs.spring.container.fifo; +package com.jashmore.sqs.annotations.core.fifo; import static org.assertj.core.api.Assertions.assertThat; import com.jashmore.sqs.container.fifo.FifoMessageListenerContainerProperties; import java.time.Duration; + +import com.jashmore.sqs.placeholder.StaticPlaceholderResolver; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.mock.env.MockEnvironment; class FifoQueueListenerParserTest { - MockEnvironment mockEnvironment; + StaticPlaceholderResolver placeholderResolver; FifoQueueListenerParser parser; @BeforeEach void setUp() { - mockEnvironment = new MockEnvironment(); - parser = new FifoQueueListenerParser(mockEnvironment); + placeholderResolver = new StaticPlaceholderResolver(); + parser = new FifoQueueListenerParser(placeholderResolver); } @Test @@ -74,11 +75,11 @@ void stringFieldsWithRawValues() throws Exception { @Test void stringFieldsWithReplacements() throws Exception { // arrange - mockEnvironment - .withProperty("queue.concurrencyLevel", "2") - .withProperty("queue.maximumMessagesInMessageGroup", "3") - .withProperty("queue.maximumCachedMessageGroups", "15") - .withProperty("queue.messageVisibilityInSeconds", "5"); + placeholderResolver + .withMapping("${queue.concurrencyLevel}", "2") + .withMapping("${queue.maximumMessagesInMessageGroup}", "3") + .withMapping("${queue.maximumCachedMessageGroups}", "15") + .withMapping("${queue.messageVisibilityInSeconds}", "5"); final FifoQueueListener annotation = FifoQueueListenerParserTest.class.getMethod("stringMethodWithReplacements").getAnnotation(FifoQueueListener.class); diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactoryTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingMessageListenerContainerFactoryTest.java similarity index 76% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactoryTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingMessageListenerContainerFactoryTest.java index 89e4ccd9..a1f33fe2 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactoryTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingMessageListenerContainerFactoryTest.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.container.prefetch; +package com.jashmore.sqs.annotations.core.prefetch; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -10,10 +10,11 @@ import com.jashmore.sqs.argument.ArgumentResolverService; import com.jashmore.sqs.container.MessageListenerContainer; import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainer; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.MessageListenerContainerInitialisationException; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.container.MessageListenerContainerInitialisationException; +import com.jashmore.sqs.placeholder.PlaceholderResolver; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.client.QueueResolver; import java.lang.reflect.Method; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -21,11 +22,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** - * Class is hard to test as it is the one building all of the dependencies internally using new constructors. Don't really know a better way to do this + * Class is hard to test as it is the one building all the dependencies internally using new constructors. Don't really know a better way to do this * without building unnecessary classes. */ @SuppressWarnings("WeakerAccess") @@ -45,21 +45,21 @@ class PrefetchingMessageListenerContainerFactoryTest { private QueueResolver queueResolver; @Mock - private Environment environment; + private PlaceholderResolver placeholderResolver; @Mock DecoratingMessageProcessorFactory decoratingMessageProcessorFactory; - private PrefetchingMessageListenerContainerFactory prefetchingMessageListenerContainerFactory; + private PrefetchingAnnotationMessageListenerContainerFactory prefetchingMessageListenerContainerFactory; @BeforeEach void setUp() { prefetchingMessageListenerContainerFactory = - new PrefetchingMessageListenerContainerFactory( + new PrefetchingAnnotationMessageListenerContainerFactory( argumentResolverService, sqsAsyncClientProvider, queueResolver, - new PrefetchingQueueListenerParser(environment), + new PrefetchingQueueListenerParser(placeholderResolver), decoratingMessageProcessorFactory ); } @@ -72,11 +72,10 @@ void canBuildMessageListenerContainer() throws NoSuchMethodException { final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("myMethod"); // act - final MessageListenerContainer messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer).isInstanceOf(PrefetchingMessageListenerContainer.class); + assertThat(messageListenerContainer).containsInstanceOf(PrefetchingMessageListenerContainer.class); } @Test @@ -87,11 +86,11 @@ void queueListenerWrapperWithoutIdentifierWillConstructOneByDefault() throws NoS final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("myMethod"); // act - final MessageListenerContainer messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer.getIdentifier()).isEqualTo("prefetching-message-listener-container-factory-test-my-method"); + assertThat(messageListenerContainer).isNotEmpty(); + assertThat(messageListenerContainer.get().getIdentifier()).isEqualTo("prefetching-message-listener-container-factory-test-my-method"); } @Test @@ -102,11 +101,11 @@ void queueListenerWrapperWithIdentifierWillUseThatForTheMessageListenerContainer final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("myMethodWithIdentifier"); // act - final MessageListenerContainer messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); - assertThat(messageListenerContainer.getIdentifier()).isEqualTo("identifier"); + assertThat(messageListenerContainer).isNotEmpty(); + assertThat(messageListenerContainer.get().getIdentifier()).isEqualTo("identifier"); } @Test @@ -127,8 +126,8 @@ void queueIsResolvedViaTheQueueResolver() throws NoSuchMethodException { void invalidConcurrencyLevelStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.concurrency}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.concurrency}")).thenReturn("Test Invalid"); final Object bean = new PrefetchingMessageListenerContainerFactoryTest(); final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -140,8 +139,8 @@ void invalidConcurrencyLevelStringFailsToWrapMessageListener() throws Exception void invalidMessageVisibilityTimeoutInSecondsStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.visibility}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.visibility}")).thenReturn("Test Invalid"); final Object bean = new PrefetchingMessageListenerContainerFactoryTest(); final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -153,8 +152,8 @@ void invalidMessageVisibilityTimeoutInSecondsStringFailsToWrapMessageListener() void invalidMaxPrefetchedMessagesStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.maxPrefetched}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.maxPrefetched}")).thenReturn("Test Invalid"); final Object bean = new PrefetchingMessageListenerContainerFactoryTest(); final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -166,8 +165,8 @@ void invalidMaxPrefetchedMessagesStringFailsToWrapMessageListener() throws Excep void invalidDesiredMinPrefetchedMessagesStringFailsToWrapMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); - when(environment.resolvePlaceholders("${prop.desiredMinPrefetchedMessages}")).thenReturn("Test Invalid"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders("${prop.desiredMinPrefetchedMessages}")).thenReturn("Test Invalid"); final Object bean = new PrefetchingMessageListenerContainerFactoryTest(); final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); @@ -179,15 +178,15 @@ void invalidDesiredMinPrefetchedMessagesStringFailsToWrapMessageListener() throw void validStringFieldsWillCorrectlyBuildMessageListener() throws Exception { // arrange when(sqsAsyncClientProvider.getDefaultClient()).thenReturn(Optional.of(defaultClient)); - when(environment.resolvePlaceholders(anyString())).thenReturn("1"); + when(placeholderResolver.resolvePlaceholders(anyString())).thenReturn("1"); final Object bean = new PrefetchingMessageListenerContainerFactoryTest(); final Method method = PrefetchingMessageListenerContainerFactoryTest.class.getMethod("methodWithFieldsUsingEnvironmentProperties"); // act - final MessageListenerContainer messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); + final Optional messageListenerContainer = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(messageListenerContainer).isNotNull(); + assertThat(messageListenerContainer).isNotEmpty(); } @Test @@ -231,10 +230,10 @@ void whenSpecificSqsClientRequestWhichCanBeFoundTheContainerCanBeBuilt() throws when(sqsAsyncClientProvider.getClient("clientId")).thenReturn(Optional.of(mock(SqsAsyncClient.class))); // act - final MessageListenerContainer container = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); + final Optional container = prefetchingMessageListenerContainerFactory.buildContainer(bean, method); // assert - assertThat(container).isNotNull(); + assertThat(container).isNotEmpty(); } @PrefetchingQueueListener("test") diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParserTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParserTest.java similarity index 87% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParserTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParserTest.java index cba10f60..4c70752c 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParserTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParserTest.java @@ -1,23 +1,29 @@ -package com.jashmore.sqs.spring.container.prefetch; +package com.jashmore.sqs.annotations.core.prefetch; import static org.assertj.core.api.Assertions.assertThat; import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainerProperties; import java.time.Duration; + +import com.jashmore.sqs.placeholder.StaticPlaceholderResolver; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.mock.env.MockEnvironment; class PrefetchingQueueListenerParserTest { - MockEnvironment mockEnvironment; + StaticPlaceholderResolver placeholderResolver = new StaticPlaceholderResolver(); PrefetchingQueueListenerParser parser; @BeforeEach void setUp() { - mockEnvironment = new MockEnvironment(); - parser = new PrefetchingQueueListenerParser(mockEnvironment); + parser = new PrefetchingQueueListenerParser(placeholderResolver); + } + + @AfterEach + void tearDown() { + placeholderResolver.reset(); } @Test @@ -75,11 +81,11 @@ void stringFieldsWithRawValues() throws Exception { @Test void stringFieldsWithReplacements() throws Exception { // arrange - mockEnvironment - .withProperty("queue.concurrencyLevel", "2") - .withProperty("queue.desiredMinPrefetchedMessages", "3") - .withProperty("queue.maxPrefetchedMessages", "15") - .withProperty("queue.messageVisibilityInSeconds", "5"); + placeholderResolver + .withMapping("${queue.concurrencyLevel}", "2") + .withMapping("${queue.desiredMinPrefetchedMessages}", "3") + .withMapping("${queue.maxPrefetchedMessages}", "15") + .withMapping("${queue.messageVisibilityInSeconds}", "5"); final PrefetchingQueueListener annotation = PrefetchingQueueListenerParserTest.class.getMethod("stringMethodWithReplacements") .getAnnotation(PrefetchingQueueListener.class); diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java b/annotations/src/test/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java similarity index 94% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java rename to annotations/src/test/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java index d5ee14e9..0f6b60d9 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java +++ b/annotations/src/test/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.decorator.visibilityextender; +package com.jashmore.sqs.annotations.decorator.visibilityextender; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; @@ -8,9 +8,10 @@ import com.jashmore.sqs.QueueProperties; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecorator; import com.jashmore.sqs.decorator.AutoVisibilityExtenderMessageProcessingDecoratorProperties; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.processor.DecoratingMessageProcessor; import com.jashmore.sqs.processor.LambdaMessageProcessor; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactoryException; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactoryException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.time.Duration; @@ -27,7 +28,6 @@ import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.ChangeMessageVisibilityBatchRequest; import software.amazon.awssdk.services.sqs.model.ChangeMessageVisibilityBatchResponse; @@ -43,13 +43,13 @@ class AutoVisibilityExtenderMessageProcessingDecoratorFactoryTest { QueueProperties queueProperties; @Mock - Environment environment; + PlaceholderResolver placeholderResolver; AutoVisibilityExtenderMessageProcessingDecoratorFactory factory; @BeforeEach void setUp() { - factory = new AutoVisibilityExtenderMessageProcessingDecoratorFactory(environment); + factory = new AutoVisibilityExtenderMessageProcessingDecoratorFactory(placeholderResolver); } @Test @@ -150,7 +150,7 @@ class BuildConfigurationProperties { @Test void shouldBeAbleToBuildPropertiesFromStrings() { // arrange - when(environment.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); + when(placeholderResolver.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); final AutoVisibilityExtender annotation = new AutoVisibilityExtender() { @Override public Class annotationType() { @@ -250,7 +250,7 @@ public String bufferTimeInSecondsString() { @Test void noMaximumDurationWillThrowException() { // arrange - when(environment.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); + when(placeholderResolver.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); final AutoVisibilityExtender annotation = new AutoVisibilityExtender() { @Override public Class annotationType() { @@ -301,7 +301,7 @@ public String bufferTimeInSecondsString() { @Test void noBufferTimeInSecondsWillThrowException() { // arrange - when(environment.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); + when(placeholderResolver.resolvePlaceholders(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); final AutoVisibilityExtender annotation = new AutoVisibilityExtender() { @Override public Class annotationType() { diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolutionException.java b/api/src/main/java/com/jashmore/sqs/client/QueueResolutionException.java similarity index 86% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolutionException.java rename to api/src/main/java/com/jashmore/sqs/client/QueueResolutionException.java index 4542892c..5e77f8a2 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolutionException.java +++ b/api/src/main/java/com/jashmore/sqs/client/QueueResolutionException.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.queue; +package com.jashmore.sqs.client; /** * Exception thrown when there was a problem resolving the URL for a queue. diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolver.java b/api/src/main/java/com/jashmore/sqs/client/QueueResolver.java similarity index 86% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolver.java rename to api/src/main/java/com/jashmore/sqs/client/QueueResolver.java index 48c4cd3b..8be5bde1 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/queue/QueueResolver.java +++ b/api/src/main/java/com/jashmore/sqs/client/QueueResolver.java @@ -1,10 +1,9 @@ -package com.jashmore.sqs.spring.queue; +package com.jashmore.sqs.client; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** - * Service that is injected into the applications dependency injection framework that can be used to resolve parameterised strings to a queue url if there is - * a queue that exists. + * Used to determine the Queue URL for a SQS client based on the name or URL. */ public interface QueueResolver { /** diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java b/api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java similarity index 97% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java rename to api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java index 896aa7cc..25e805ca 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java +++ b/api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.client; +package com.jashmore.sqs.client; import java.util.Optional; import software.amazon.awssdk.services.sqs.SqsAsyncClient; diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java similarity index 80% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java rename to api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java index 6080ca73..9ef222b4 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java +++ b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java @@ -1,6 +1,5 @@ -package com.jashmore.sqs.spring.container; +package com.jashmore.sqs.container; -import com.jashmore.sqs.container.MessageListenerContainer; import java.util.Set; /** @@ -9,7 +8,7 @@ */ public interface MessageListenerContainerCoordinator { /** - * Get all of the containers that have been configured with this coordinator. + * Get all the containers that have been configured with this coordinator. * * @return the set of containers */ @@ -29,7 +28,7 @@ public interface MessageListenerContainerCoordinator { void startContainer(String queueIdentifier); /** - * Stop all of the containers that have been built for the application. + * Stop all the containers that have been built for the application. */ void stopAllContainers(); diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java similarity index 51% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java rename to api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java index 46f39066..94e9bbfb 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java +++ b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java @@ -1,30 +1,20 @@ -package com.jashmore.sqs.spring.container; +package com.jashmore.sqs.container; -import com.jashmore.sqs.container.MessageListenerContainer; import java.lang.reflect.Method; +import java.util.Optional; /** * Wrapper used to analyse methods in the application and determine if the method should be included in the messaging framework by wrapping that method in a * queue listener so that each message received will run the method. */ public interface MessageListenerContainerFactory { - /** - * Determine whether the method can be used to handle listening to queues. - * - *

This could be checking that the method is in a certain format or has an annotation indicating that it can be wrapped. - * - * @param method the method to check against - * @return whether this method should be wrapped in a queue listener - */ - boolean canHandleMethod(Method method); - /** * Builds a {@link MessageListenerContainer} that will wrap the method and all messages for a queue will execute this method. * * @param bean the specific bean for this method * @param method the method of the bean that will be run for each message - * @return the container that will wrap this method + * @return the container that will wrap this method if it can * @throws MessageListenerContainerInitialisationException if there was a known error building this container */ - MessageListenerContainer buildContainer(Object bean, Method method) throws MessageListenerContainerInitialisationException; + Optional buildContainer(Object bean, Method method) throws MessageListenerContainerInitialisationException; } diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerInitialisationException.java b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerInitialisationException.java similarity index 72% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerInitialisationException.java rename to api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerInitialisationException.java index e9074568..1cfbaf01 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerInitialisationException.java +++ b/api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerInitialisationException.java @@ -1,7 +1,7 @@ -package com.jashmore.sqs.spring.container; +package com.jashmore.sqs.container; /** - * Exception that is thrown when their is a known error initialising a {@link com.jashmore.sqs.container.MessageListenerContainer}. + * Exception that is thrown when there is a known error initialising a {@link com.jashmore.sqs.container.MessageListenerContainer}. */ public class MessageListenerContainerInitialisationException extends RuntimeException { diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactory.java b/api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactory.java similarity index 92% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactory.java rename to api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactory.java index 034542f2..0ecc919e 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactory.java +++ b/api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactory.java @@ -1,7 +1,7 @@ -package com.jashmore.sqs.spring.decorator; +package com.jashmore.sqs.decorator; import com.jashmore.sqs.QueueProperties; -import com.jashmore.sqs.decorator.MessageProcessingDecorator; + import java.lang.reflect.Method; import java.util.Optional; import software.amazon.awssdk.services.sqs.SqsAsyncClient; diff --git a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactoryException.java b/api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactoryException.java similarity index 90% rename from spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactoryException.java rename to api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactoryException.java index 61f2e85d..657fd616 100644 --- a/spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactoryException.java +++ b/api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactoryException.java @@ -1,6 +1,4 @@ -package com.jashmore.sqs.spring.decorator; - -import com.jashmore.sqs.decorator.MessageProcessingDecorator; +package com.jashmore.sqs.decorator; /** * An exception thrown if there was an error building the {@link MessageProcessingDecorator} or if the method being diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/queue/DefaultQueueResolver.java b/core/src/main/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolver.java similarity index 55% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/queue/DefaultQueueResolver.java rename to core/src/main/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolver.java index 542d5b8a..4b4367a6 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/queue/DefaultQueueResolver.java +++ b/core/src/main/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolver.java @@ -1,24 +1,25 @@ -package com.jashmore.sqs.spring.queue; +package com.jashmore.sqs.client; import java.util.concurrent.ExecutionException; -import lombok.AllArgsConstructor; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; + +import com.jashmore.sqs.placeholder.PlaceholderResolver; import software.amazon.awssdk.services.sqs.SqsAsyncClient; /** - * Default implementation that uses the {@link Environment} to resolve the placeholders in a string and from that either - * return the URL if it is a URL or uses the value to try and get the URL from the SQS server. + * Default implementation that allows placeholders to be included in the string that will be replaced before + * calling out to the SQS server to get the Queue URL. */ -@Service -@AllArgsConstructor -public class DefaultQueueResolver implements QueueResolver { +public class DefaultPlaceholderQueueResolver implements QueueResolver { + + private final PlaceholderResolver placeholderResolver; - private final Environment environment; + public DefaultPlaceholderQueueResolver(final PlaceholderResolver placeholderResolver) { + this.placeholderResolver = placeholderResolver; + } @Override public String resolveQueueUrl(final SqsAsyncClient sqsAsyncClient, final String queueNameOrUrl) { - final String resolvedQueueNameOrUrl = environment.resolveRequiredPlaceholders(queueNameOrUrl); + final String resolvedQueueNameOrUrl = placeholderResolver.resolvePlaceholders(queueNameOrUrl); if (resolvedQueueNameOrUrl.startsWith("http")) { return resolvedQueueNameOrUrl; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProvider.java b/core/src/main/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProvider.java similarity index 97% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProvider.java rename to core/src/main/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProvider.java index cfc8e5ce..22b6d1e7 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProvider.java +++ b/core/src/main/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProvider.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.client; +package com.jashmore.sqs.client; import com.jashmore.sqs.util.Preconditions; import java.util.Collections; diff --git a/core/src/main/java/com/jashmore/sqs/placeholder/PlaceholderResolver.java b/core/src/main/java/com/jashmore/sqs/placeholder/PlaceholderResolver.java new file mode 100644 index 00000000..2b319157 --- /dev/null +++ b/core/src/main/java/com/jashmore/sqs/placeholder/PlaceholderResolver.java @@ -0,0 +1,12 @@ +package com.jashmore.sqs.placeholder; + +@FunctionalInterface +public interface PlaceholderResolver { + /** + * Resolve placeholders in the provided text. E.g. ${something.url} may be resolved to https://localhost:9090. + * + * @param text the text to parse and replace + * @return the new text with placeholders replaced + */ + String resolvePlaceholders(String text); +} diff --git a/core/src/main/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolver.java b/core/src/main/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolver.java new file mode 100644 index 00000000..1fe0f90f --- /dev/null +++ b/core/src/main/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolver.java @@ -0,0 +1,42 @@ +package com.jashmore.sqs.placeholder; + +import com.jashmore.documentation.annotations.Nonnull; + +import java.util.HashMap; +import java.util.Map; + +/** + * Simple implementation of a {@link PlaceholderResolver} that just does a simple string + * replace from a static map of values. This is mostly only useful for testing. + */ +public class StaticPlaceholderResolver implements PlaceholderResolver { + private final Map placeholderMap; + + public StaticPlaceholderResolver() { + this.placeholderMap = new HashMap<>(); + } + + public StaticPlaceholderResolver withMapping(final String from, final String to) { + placeholderMap.put(from, to); + return this; + } + + public void reset() { + this.placeholderMap.clear(); + } + + @Override + public String resolvePlaceholders(@Nonnull final String text) { + String previousText = text; + String mutatedText = null; + while (!previousText.equals(mutatedText)) { + mutatedText = mutatedText == null ? text : mutatedText; + previousText = mutatedText; + + for (Map.Entry entry : placeholderMap.entrySet()) { + mutatedText = mutatedText.replace(entry.getKey(), entry.getValue()); + } + } + return mutatedText; + } +} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactory.java b/core/src/main/java/com/jashmore/sqs/processor/DecoratingMessageProcessorFactory.java similarity index 90% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactory.java rename to core/src/main/java/com/jashmore/sqs/processor/DecoratingMessageProcessorFactory.java index 16b2d8ed..bb749c8d 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactory.java +++ b/core/src/main/java/com/jashmore/sqs/processor/DecoratingMessageProcessorFactory.java @@ -1,10 +1,8 @@ -package com.jashmore.sqs.spring.processor; +package com.jashmore.sqs.processor; import com.jashmore.sqs.QueueProperties; import com.jashmore.sqs.decorator.MessageProcessingDecorator; -import com.jashmore.sqs.processor.DecoratingMessageProcessor; -import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactory; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactory; import com.jashmore.sqs.util.collections.CollectionUtils; import java.lang.reflect.Method; import java.util.List; diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/queue/DefaultQueueResolverTest.java b/core/src/test/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolverTest.java similarity index 77% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/queue/DefaultQueueResolverTest.java rename to core/src/test/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolverTest.java index a7e482ef..2120dd7d 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/queue/DefaultQueueResolverTest.java +++ b/core/src/test/java/com/jashmore/sqs/client/DefaultPlaceholderQueueResolverTest.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.queue; +package com.jashmore.sqs.client; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -7,40 +7,41 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; + +import com.jashmore.sqs.placeholder.PlaceholderResolver; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.env.Environment; import software.amazon.awssdk.services.sqs.SqsAsyncClient; import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; @ExtendWith(MockitoExtension.class) -class DefaultQueueResolverTest { +class DefaultPlaceholderQueueResolverTest { @Mock private SqsAsyncClient sqsAsyncClient; @Mock - private Environment environment; + private PlaceholderResolver placeholderResolver; - private DefaultQueueResolver defaultQueueResolver; + private DefaultPlaceholderQueueResolver defaultPlaceholderQueueResolver; @BeforeEach void setUp() { - defaultQueueResolver = new DefaultQueueResolver(environment); + defaultPlaceholderQueueResolver = new DefaultPlaceholderQueueResolver(placeholderResolver); } @Test void resolvedValueThatIsAUrlIsReturned() { // arrange - when(environment.resolveRequiredPlaceholders("${variable}")).thenReturn("http://url"); + when(placeholderResolver.resolvePlaceholders("${variable}")).thenReturn("http://url"); // act - final String queueUrl = defaultQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}"); + final String queueUrl = defaultPlaceholderQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}"); // assert assertThat(queueUrl).isEqualTo("http://url"); @@ -49,12 +50,12 @@ void resolvedValueThatIsAUrlIsReturned() { @Test void resolvedValueThatIsNotAUrlCallsOutToAmazonForUrl() { // arrange - when(environment.resolveRequiredPlaceholders("${variable}")).thenReturn("someQueueName"); + when(placeholderResolver.resolvePlaceholders("${variable}")).thenReturn("someQueueName"); when(sqsAsyncClient.getQueueUrl(ArgumentMatchers.>any())) .thenReturn(CompletableFuture.completedFuture(GetQueueUrlResponse.builder().queueUrl("http://url").build())); // act - final String queueUrl = defaultQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}"); + final String queueUrl = defaultPlaceholderQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}"); // assert assertThat(queueUrl).isEqualTo("http://url"); @@ -63,7 +64,7 @@ void resolvedValueThatIsNotAUrlCallsOutToAmazonForUrl() { @Test void exceptionThrownWhileGettingQueueUrlBubblesException() { // arrange - when(environment.resolveRequiredPlaceholders("${variable}")).thenReturn("someQueueName"); + when(placeholderResolver.resolvePlaceholders("${variable}")).thenReturn("someQueueName"); final RuntimeException exceptionCause = new RuntimeException("error"); when(sqsAsyncClient.getQueueUrl(ArgumentMatchers.>any())) .thenReturn( @@ -78,7 +79,7 @@ public GetQueueUrlResponse get() throws ExecutionException { // act final QueueResolutionException exception = assertThrows( QueueResolutionException.class, - () -> defaultQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}") + () -> defaultPlaceholderQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}") ); // assert @@ -88,7 +89,7 @@ public GetQueueUrlResponse get() throws ExecutionException { @Test void interruptedThreadWhileGettingQueueUrlThrowsQueueResolutionException() { // arrange - when(environment.resolveRequiredPlaceholders("${variable}")).thenReturn("someQueueName"); + when(placeholderResolver.resolvePlaceholders("${variable}")).thenReturn("someQueueName"); final InterruptedException exceptionCause = new InterruptedException(); when(sqsAsyncClient.getQueueUrl(ArgumentMatchers.>any())) .thenReturn( @@ -103,7 +104,7 @@ public GetQueueUrlResponse get() throws ExecutionException { // act final QueueResolutionException exception = assertThrows( QueueResolutionException.class, - () -> defaultQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}") + () -> defaultPlaceholderQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}") ); // assert @@ -113,7 +114,7 @@ public GetQueueUrlResponse get() throws ExecutionException { @Test void interruptedThreadWhileGettingQueueUrlShouldMaintainThreadInterruptedBoolean() { // arrange - when(environment.resolveRequiredPlaceholders("${variable}")).thenReturn("someQueueName"); + when(placeholderResolver.resolvePlaceholders("${variable}")).thenReturn("someQueueName"); final InterruptedException interruptedException = new InterruptedException(); when(sqsAsyncClient.getQueueUrl(ArgumentMatchers.>any())) .thenReturn( @@ -126,7 +127,7 @@ public GetQueueUrlResponse get() throws InterruptedException { ); // act - assertThrows(QueueResolutionException.class, () -> defaultQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}")); + assertThrows(QueueResolutionException.class, () -> defaultPlaceholderQueueResolver.resolveQueueUrl(sqsAsyncClient, "${variable}")); // assert assertThat(Thread.currentThread().isInterrupted()).isTrue(); diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProviderTest.java b/core/src/test/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProviderTest.java similarity index 99% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProviderTest.java rename to core/src/test/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProviderTest.java index d94a60d3..98cbb161 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/client/DefaultSqsAsyncClientProviderTest.java +++ b/core/src/test/java/com/jashmore/sqs/client/DefaultSqsAsyncClientProviderTest.java @@ -1,4 +1,4 @@ -package com.jashmore.sqs.spring.client; +package com.jashmore.sqs.client; import static org.assertj.core.api.Assertions.assertThat; @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; diff --git a/core/src/test/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolverTest.java b/core/src/test/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolverTest.java new file mode 100644 index 00000000..2367f43b --- /dev/null +++ b/core/src/test/java/com/jashmore/sqs/placeholder/StaticPlaceholderResolverTest.java @@ -0,0 +1,36 @@ +package com.jashmore.sqs.placeholder; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class StaticPlaceholderResolverTest { + + @Test + void canReplacePlaceholdersFromStaticList() { + assertThat(new StaticPlaceholderResolver().withMapping("from", "to").resolvePlaceholders("from something")) + .isEqualTo("to something"); + } + + @Test + void resettingPlaceholdersWillClearMapping() { + final StaticPlaceholderResolver placeholderResolver = new StaticPlaceholderResolver() + .withMapping("from", "to"); + + placeholderResolver.reset(); + + assertThat(placeholderResolver.resolvePlaceholders("from something")) + .isEqualTo("from something"); + } + + @Test + void canChangePlaceholderReplacements() { + final StaticPlaceholderResolver placeholderResolver = new StaticPlaceholderResolver() + .withMapping("first", "second") + .withMapping("second", "third word") + .withMapping("third", "fourth"); + + assertThat(placeholderResolver.resolvePlaceholders("first for us")) + .isEqualTo("fourth word for us"); + } +} \ No newline at end of file diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java b/core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java similarity index 95% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java rename to core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java index ce604e6a..0e81e2ed 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java +++ b/core/src/test/java/com/jashmore/sqs/spring/processor/DecoratingMessageProcessorFactoryTest.java @@ -6,8 +6,9 @@ import com.jashmore.sqs.QueueProperties; import com.jashmore.sqs.decorator.MessageProcessingDecorator; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactory; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactory; import java.util.Collections; import java.util.Optional; import java.util.concurrent.CompletableFuture; diff --git a/doc/how-to-guides/core/core-how-to-use-kotlin-dsl.md b/doc/how-to-guides/core/core-how-to-use-kotlin-dsl.md index 0534140a..c8a32acf 100644 --- a/doc/how-to-guides/core/core-how-to-use-kotlin-dsl.md +++ b/doc/how-to-guides/core/core-how-to-use-kotlin-dsl.md @@ -70,7 +70,7 @@ val container = coreMessageListener("identifier", sqsAsyncClient, queueUrl) { ## Using the batchingMessageListener This is equivalent to -the [@QueueListener](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java) annotation +the [@QueueListener](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListener.java) annotation used in a Spring Boot application which will set up a container that will request for messages in batches. ```kotlin @@ -90,7 +90,7 @@ val container = batchingMessageListener("identifier", sqsAsyncClient, "url") { ## Using the prefetchingMessageListener This is equivalent to -the [@PrefetchingQueueListener](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java) annotation +the [@PrefetchingQueueListener](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java) annotation used in a Spring Boot application which will set up a container that will prefetch messages for processing. ```kotlin diff --git a/doc/how-to-guides/spring/spring-how-to-add-aws-xray-tracing.md b/doc/how-to-guides/spring/spring-how-to-add-aws-xray-tracing.md index 64ba8082..b84205da 100644 --- a/doc/how-to-guides/spring/spring-how-to-add-aws-xray-tracing.md +++ b/doc/how-to-guides/spring/spring-how-to-add-aws-xray-tracing.md @@ -11,7 +11,7 @@ For more information about each component this is auto configuring for you, take 1. Add the [AWS XRay Spring Boot Extension](../../../extensions/aws-xray-extension/spring-boot) as a dependency. This will auto configure a [BasicXrayMessageProcessingDecorator](../../../extensions/aws-xray-extension/core/src/main/java/com/jashmore/sqs/extensions/xray/decorator/BasicXrayMessageProcessingDecorator.java) - and a [SqsAsyncClientProvider](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java) which will wrap + and a [SqsAsyncClientProvider](../../../api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java) which will wrap the `SqsAsyncClient` in a [XrayWrappedSqsAsyncClient](../../../extensions/aws-xray-extension/core/src/main/java/com/jashmore/sqs/extensions/xray/client/XrayWrappedSqsAsyncClient.java). @@ -117,7 +117,7 @@ public class MyConfiguration { ### Communicating with Multiple AWS Accounts As seen in [Spring - How to connect to multiple AWS Accounts](spring-how-to-connect-to-multiple-aws-accounts.md), you can create a custom -[SqsAsyncClientProvider](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java) to communicate with multiple +[SqsAsyncClientProvider](../../../api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java) to communicate with multiple SQS queues across multiple accounts. If you are have included the [AWS Xray SDK V2 Instrumentor](https://github.com/aws/aws-xray-sdk-java/tree/master/aws-xray-recorder-sdk-aws-sdk-v2-instrumentor) dependency you will need to make sure to wrap each `SqsAsyncClient` in an diff --git a/doc/how-to-guides/spring/spring-how-to-add-custom-message-processing-decorators.md b/doc/how-to-guides/spring/spring-how-to-add-custom-message-processing-decorators.md index 7c5351b2..b46d3fd9 100644 --- a/doc/how-to-guides/spring/spring-how-to-add-custom-message-processing-decorators.md +++ b/doc/how-to-guides/spring/spring-how-to-add-custom-message-processing-decorators.md @@ -21,7 +21,7 @@ public class MyConfiguration { It may not be desirable to have a [MessageProcessingDecorator](../../../api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecorator.java) attached to every listener and instead you want to apply it to only a subset of listeners. This can be achieved implementing a -[MessageProcessingDecoratorFactory](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/decorator/MessageProcessingDecoratorFactory.java) which +[MessageProcessingDecoratorFactory](../../../api/src/main/java/com/jashmore/sqs/decorator/MessageProcessingDecoratorFactory.java) which will have the opportunity to wrap a message listener, e.g. by looking for an annotation. An example decorator which will wrap any message listener that has the `MyAnnotation` annotation. diff --git a/doc/how-to-guides/spring/spring-how-to-add-own-queue-listener.md b/doc/how-to-guides/spring/spring-how-to-add-own-queue-listener.md index 60c18f2a..239b0990 100644 --- a/doc/how-to-guides/spring/spring-how-to-add-own-queue-listener.md +++ b/doc/how-to-guides/spring/spring-how-to-add-own-queue-listener.md @@ -1,6 +1,6 @@ # Spring - How to add your own Queue Listener -The [MessageListenerContainerFactory](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java) is +The [MessageListenerContainerFactory](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java) is used to process bean methods to determine whether they should be used to process SQS messages. The most common way to define whether a method should be wrapped is via an annotation, for example the following method indicates that it should be wrapped in a basic queue listener: @@ -16,7 +16,7 @@ public class MyService { ``` -There are only a few core implementations of the [MessageListenerContainerFactory](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java) +There are only a few core implementations of the [MessageListenerContainerFactory](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java) and therefore there may be use cases that are not covered. Consumers can define their own factory that will be used alongside the core ones provided. ## Example Use Case @@ -25,10 +25,10 @@ An application wants to provide a custom annotation to wrap methods with some me ## Prerequisites -1. This relies on no custom [MessageListenerContainerCoordinator](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java) +1. This relies on no custom [MessageListenerContainerCoordinator](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java) being provided by the consumer of this framework and therefore the default will be used. See [QueueListenerConfiguration](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/config/QueueListenerConfiguration.java) - for more information about how the [MessageListenerContainerFactory](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java) + for more information about how the [MessageListenerContainerFactory](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java) are collected. ## Steps @@ -56,50 +56,11 @@ we wrote. ``` -1. Implement the [MessageListenerContainerFactory](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerFactory.java) - interface with the custom implementation. As we are using annotations to indicate the method to wrap, we can extend - the [AbstractAnnotationMessageListenerContainerFactory](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractAnnotationMessageListenerContainerFactory.java) - to make matching on annotations easier. At this point whenever we enter `wrapMethodContainingAnnotation` we have found a bean with a method - annotated with `SleepingQueueListener` and now we need to create - a [MessageListenerContainer](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainer.java) - that has the responsibility of handling the spring lifecycle of this queue listener. The - [CoreMessageListenerContainer](../../../core/src/main/java/com/jashmore/sqs/container/CoreMessageListenerContainer.java) - is the simplest (and currently only) implementation of this and is the most likely one to use. - - ```java - public class MySleepingMessageListenerContainerFactory - extends AbstractAnnotationMessageListenerContainerFactory { - - private final ArgumentResolverService argumentResolverService; - private final SqsAsyncClientProvider sqsAsyncClientProvider; - - public MySleepingMessageListenerContainerFactory( - final ArgumentResolverService argumentResolverService, - final SqsAsyncClientProvider sqsAsyncClientProvider - ) { - this.argumentResolverService = argumentResolverService; - this.sqsAsyncClientProvider = sqsAsyncClientProvider; - } - - @Override - protected Class getAnnotationClass() { - return SleepingQueueListener.class; - } - - @Override - protected MessageListenerContainer wrapMethodContainingAnnotation( - final Object bean, - final Method method, - final SleepingQueueListener annotation - ) { - // build your container here - return new CoreMessageListenerContainer( - // components in here - ); - } - } - - ``` +1. Implement the [MessageListenerContainerFactory](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerFactory.java) + interface with the custom implementation. As we are using annotations to indicate the method to wrap, we can use + the [AnnotationMessageListenerContainerFactory](../../../annotations/src/main/java/com/jashmore/sqs/annotations/container/AnnotationMessageListenerContainerFactory.java) + to make matching on annotations easier. See the [Basic](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/BasicAnnotationMessageListenerContainerFactory.java + for a way to implement this. 1. Add this bean into the application context by annotating it with the `@Component` annotation or by defining it as a spring bean in a `@Configuration` class. @@ -111,10 +72,9 @@ we wrote. public MessageListenerContainerFactory mySleepingMessageListenerContainerFactory( final ArgumentResolverService argumentResolverService ) { - return new MySleepingMessageListenerContainerFactory(argumentResolverService); + return new MySleepingMessageListenerContainerFactory(...); } } - ``` To see how the containers get built, take a look at the diff --git a/doc/how-to-guides/spring/spring-how-to-connect-to-multiple-aws-accounts.md b/doc/how-to-guides/spring/spring-how-to-connect-to-multiple-aws-accounts.md index 818a5bc5..ba2f7814 100644 --- a/doc/how-to-guides/spring/spring-how-to-connect-to-multiple-aws-accounts.md +++ b/doc/how-to-guides/spring/spring-how-to-connect-to-multiple-aws-accounts.md @@ -32,7 +32,7 @@ connect to two locally running ElasticMQ servers. ``` 1. You will need to add to your `@Configuration` a bean of type - [SqsAsyncClientProvider](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/client/SqsAsyncClientProvider.java) + [SqsAsyncClientProvider](../../../api/src/main/java/com/jashmore/sqs/client/SqsAsyncClientProvider.java) which will provide all of the `SqsAsyncClient`s for the queues above. ```java diff --git a/doc/how-to-guides/spring/spring-how-to-extend-message-visibility-during-processing.md b/doc/how-to-guides/spring/spring-how-to-extend-message-visibility-during-processing.md index 7fce20e4..b91d445d 100644 --- a/doc/how-to-guides/spring/spring-how-to-extend-message-visibility-during-processing.md +++ b/doc/how-to-guides/spring/spring-how-to-extend-message-visibility-during-processing.md @@ -32,7 +32,7 @@ public class MyClass { It may be undesirable to have to manually handle the visibility timeout as it is more development effort on the consumer's side, and it may not even be easily possible due to the consumption of third party libraries. The -[@AutoVisibilityExtender](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/decorator/visibilityextender/AutoVisibilityExtender.java) annotation +[@AutoVisibilityExtender](../../../annotations/src/main/java/com/jashmore/sqs/annotations/decorator/visibilityextender/AutoVisibilityExtender.java) annotation can be used with one of the core queue listener annotations. Note that this decorator only works for synchronous message listeners and will have unsafe functionality when used with an asynchronous message listener, diff --git a/doc/how-to-guides/spring/spring-how-to-have-listener-dynamic-properties.md b/doc/how-to-guides/spring/spring-how-to-have-listener-dynamic-properties.md index 75d0a405..2662221e 100644 --- a/doc/how-to-guides/spring/spring-how-to-have-listener-dynamic-properties.md +++ b/doc/how-to-guides/spring/spring-how-to-have-listener-dynamic-properties.md @@ -2,9 +2,9 @@ Java annotations and Spring configuration properties can't provide dynamic properties, and therefore to provide dynamic runtime configuration in Spring apps, the annotation parser can be overridden. Some annotation parsers -are [QueueListenerParser](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/QueueListener.java), -[PrefetchingQueueListenerParser](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListenerParser.java), -and [FifoQueueListenerParser](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoQueueListenerParser.java). +are [QueueListenerParser](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/basic/QueueListenerParser.java), +[PrefetchingQueueListenerParser](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListenerParser.java), +and [FifoQueueListenerParser](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/fifo/FifoQueueListenerParser.java). ## Steps @@ -39,5 +39,5 @@ and [FifoQueueListenerParser](../../../spring/spring-core/src/main/java/com/jash ``` -1. Now all message listeners with the [PrefetchingQueueListener](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingQueueListener.java) +1. Now all message listeners with the [PrefetchingQueueListener](../../../annotations/src/main/java/com/jashmore/sqs/annotations/core/prefetch/PrefetchingQueueListener.java) annotation will use a random concurrency rate. diff --git a/doc/how-to-guides/spring/spring-how-to-prevent-containers-starting-on-startup.md b/doc/how-to-guides/spring/spring-how-to-prevent-containers-starting-on-startup.md index 9d773036..d0e5541d 100644 --- a/doc/how-to-guides/spring/spring-how-to-prevent-containers-starting-on-startup.md +++ b/doc/how-to-guides/spring/spring-how-to-prevent-containers-starting-on-startup.md @@ -1,13 +1,13 @@ # Spring - How to prevent Message Listener Containers starting on startup -By default, a [DefaultMessageListenerCoordinator](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinator.java) +By default, a [SpringMessageListenerContainerCoordinator.java](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinator.java) is configured to discover containers as well as start and stop the containers in the Spring Lifecycle. This will auto startup the containers by default but, if this is not desirable, you can supply your own properties to disable this functionality. ## Steps 1. Define your own - [DefaultMessageListenerCoordinatorProperties](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorProperties.java) + [SpringMessageListenerContainerCoordinatorProperties.java](../../../spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorProperties.java) with the configuration you desire. ```java @@ -15,13 +15,13 @@ if this is not desirable, you can supply your own properties to disable this fun class MyConfiguration { @Bean - DefaultMessageListenerCoordinatorProperties defaultMessageListenerCoordinatorProperties() { - return StaticDefaultMessageListenerContainerCoordinatorProperties.builder().isAutoStartContainersEnabled(false).build(); + SpringMessageListenerContainerCoordinatorProperties springMessageListenerContainerCoordinatorProperties() { + return StaticSpringMessageListenerContainerCoordinatorProperties.builder().isAutoStartContainersEnabled(false).build(); } } ``` This will not work if you have supplied your -own [MessageListenerContainerCoordinator](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java) +own [MessageListenerContainerCoordinator](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java) bean as the default coordinator will now not be configured for you anymore. diff --git a/doc/how-to-guides/spring/spring-how-to-start-stop-message-listener-containers.md b/doc/how-to-guides/spring/spring-how-to-start-stop-message-listener-containers.md index c3ad103c..4baae011 100644 --- a/doc/how-to-guides/spring/spring-how-to-start-stop-message-listener-containers.md +++ b/doc/how-to-guides/spring/spring-how-to-start-stop-message-listener-containers.md @@ -2,7 +2,7 @@ [MessageListenerContainer](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainer.java) can be started and stopped while the Spring application is executing via the -[MessageListenerContainerCoordinator](../../../spring/spring-api/src/main/java/com/jashmore/sqs/spring/container/MessageListenerContainerCoordinator.java). +[MessageListenerContainerCoordinator](../../../api/src/main/java/com/jashmore/sqs/container/MessageListenerContainerCoordinator.java). Each container has a unique identifier and this can be used to indicate which container to start or stop. The core message listeners allow for a custom identifier to be supplied, otherwise a default will be generated from the class and method name of the message listener. diff --git a/examples/auto-visibility-extender-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/auto-visibility-extender-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index 00dea44a..d73af7f8 100644 --- a/examples/auto-visibility-extender-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/auto-visibility-extender-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -2,15 +2,16 @@ import static com.jashmore.sqs.examples.Application.QUEUE_NAME; -import com.jashmore.sqs.argument.attribute.MessageSystemAttribute; -import com.jashmore.sqs.argument.messageid.MessageId; -import com.jashmore.sqs.processor.argument.Acknowledge; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.decorator.visibilityextender.AutoVisibilityExtender; import java.time.Duration; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; + +import com.jashmore.sqs.annotations.core.basic.QueueListener; +import com.jashmore.sqs.annotations.decorator.visibilityextender.AutoVisibilityExtender; +import com.jashmore.sqs.argument.attribute.MessageSystemAttribute; +import com.jashmore.sqs.argument.messageid.MessageId; +import com.jashmore.sqs.processor.argument.Acknowledge; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import software.amazon.awssdk.services.sqs.model.MessageSystemAttributeName; diff --git a/examples/aws-xray-spring-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/aws-xray-spring-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index 2df08539..418f96ba 100644 --- a/examples/aws-xray-spring-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/aws-xray-spring-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -3,7 +3,7 @@ import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; import com.amazonaws.xray.entities.Segment; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import java.util.concurrent.CompletableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index 847103fc..44286368 100644 --- a/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -1,7 +1,7 @@ package com.jashmore.sqs.examples; +import com.jashmore.sqs.annotations.core.fifo.FifoQueueListener; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.fifo.FifoQueueListener; import com.jashmore.sqs.util.ExpectedTestException; import java.util.Map; import java.util.Random; diff --git a/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java b/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java index 7ad5f24e..26429bcf 100644 --- a/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java +++ b/examples/fifo-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java @@ -1,6 +1,6 @@ package com.jashmore.sqs.examples; -import com.jashmore.sqs.spring.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; diff --git a/examples/spring-aws-example/src/main/java/com/jashmore/examples/spring/aws/Application.java b/examples/spring-aws-example/src/main/java/com/jashmore/examples/spring/aws/Application.java index 5ef8f5d8..1db4e81c 100644 --- a/examples/spring-aws-example/src/main/java/com/jashmore/examples/spring/aws/Application.java +++ b/examples/spring-aws-example/src/main/java/com/jashmore/examples/spring/aws/Application.java @@ -1,7 +1,7 @@ package com.jashmore.examples.spring.aws; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/examples/spring-cloud-schema-registry-example/spring-cloud-schema-registry-consumer/src/main/java/com/jashmore/sqs/examples/schemaregistry/ConsumerApplication.java b/examples/spring-cloud-schema-registry-example/spring-cloud-schema-registry-consumer/src/main/java/com/jashmore/sqs/examples/schemaregistry/ConsumerApplication.java index 32fc6a07..d4f42671 100644 --- a/examples/spring-cloud-schema-registry-example/spring-cloud-schema-registry-consumer/src/main/java/com/jashmore/sqs/examples/schemaregistry/ConsumerApplication.java +++ b/examples/spring-cloud-schema-registry-example/spring-cloud-schema-registry-consumer/src/main/java/com/jashmore/sqs/examples/schemaregistry/ConsumerApplication.java @@ -3,7 +3,7 @@ import com.example.Sensor; import com.jashmore.sqs.extensions.registry.SpringCloudSchemaRegistryPayload; import com.jashmore.sqs.extensions.registry.avro.EnableSchemaRegistrySqsExtension; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClientImpl; import com.jashmore.sqs.util.SqsQueuesConfig; import lombok.extern.slf4j.Slf4j; diff --git a/examples/spring-integration-test-example/src/main/java/com/jashmore/sqs/examples/integrationtests/IntegrationTestExampleApplication.java b/examples/spring-integration-test-example/src/main/java/com/jashmore/sqs/examples/integrationtests/IntegrationTestExampleApplication.java index fc671353..fa66f196 100644 --- a/examples/spring-integration-test-example/src/main/java/com/jashmore/sqs/examples/integrationtests/IntegrationTestExampleApplication.java +++ b/examples/spring-integration-test-example/src/main/java/com/jashmore/sqs/examples/integrationtests/IntegrationTestExampleApplication.java @@ -1,7 +1,7 @@ package com.jashmore.sqs.examples.integrationtests; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; diff --git a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/Application.java b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/Application.java index 5bc5799b..04ea2a2a 100644 --- a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/Application.java +++ b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/Application.java @@ -1,8 +1,8 @@ package com.jashmore.sqs.examples; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; -import com.jashmore.sqs.spring.client.DefaultSqsAsyncClientProvider; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.DefaultSqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; diff --git a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index c5cc6073..4fa06189 100644 --- a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -1,7 +1,7 @@ package com.jashmore.sqs.examples; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java index 074eb3b8..d3d2285b 100644 --- a/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java +++ b/examples/spring-multiple-aws-account-example/src/main/java/com/jashmore/sqs/examples/ScheduledMessageProducer.java @@ -1,6 +1,6 @@ package com.jashmore.sqs.examples; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; diff --git a/examples/spring-sleuth-example/src/main/java/com/jashmore/sqs/examples/sleuth/MessageListeners.java b/examples/spring-sleuth-example/src/main/java/com/jashmore/sqs/examples/sleuth/MessageListeners.java index fc3d2e67..4e18260a 100644 --- a/examples/spring-sleuth-example/src/main/java/com/jashmore/sqs/examples/sleuth/MessageListeners.java +++ b/examples/spring-sleuth-example/src/main/java/com/jashmore/sqs/examples/sleuth/MessageListeners.java @@ -2,7 +2,7 @@ import brave.ScopedSpan; import brave.Tracing; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import java.util.UUID; import java.util.concurrent.TimeUnit; import lombok.AllArgsConstructor; diff --git a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/Application.java b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/Application.java index 3f34116a..60ff6333 100644 --- a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/Application.java +++ b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/Application.java @@ -1,8 +1,8 @@ package com.jashmore.sqs.examples; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClientImpl; import com.jashmore.sqs.util.SqsQueuesConfig; import lombok.extern.slf4j.Slf4j; diff --git a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index 65ee805f..2a5b5832 100644 --- a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -1,8 +1,8 @@ package com.jashmore.sqs.examples; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/ScheduledQueueListenerEnabler.java b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/ScheduledQueueListenerEnabler.java index 8c2e6b6d..c992eff5 100644 --- a/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/ScheduledQueueListenerEnabler.java +++ b/examples/spring-starter-example/src/main/java/com/jashmore/sqs/examples/ScheduledQueueListenerEnabler.java @@ -1,6 +1,6 @@ package com.jashmore.sqs.examples; -import com.jashmore.sqs.spring.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; diff --git a/examples/spring-starter-minimal-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java b/examples/spring-starter-minimal-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java index c2953f87..938ab44c 100644 --- a/examples/spring-starter-minimal-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java +++ b/examples/spring-starter-minimal-example/src/main/java/com/jashmore/sqs/examples/MessageListeners.java @@ -1,7 +1,7 @@ package com.jashmore.sqs.examples; import com.jashmore.sqs.argument.payload.Payload; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/extensions/aws-xray-extension/spring-boot/build.gradle.kts b/extensions/aws-xray-extension/spring-boot/build.gradle.kts index b0a99930..73277e19 100644 --- a/extensions/aws-xray-extension/spring-boot/build.gradle.kts +++ b/extensions/aws-xray-extension/spring-boot/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { api(project(":aws-xray-extension-core")) implementation("org.springframework:spring-context") implementation("org.springframework.boot:spring-boot-autoconfigure") + implementation(project(":java-dynamic-sqs-listener-core")) implementation(project(":java-dynamic-sqs-listener-spring-core")) testImplementation("org.checkerframework:checker-qual:3.7.1") diff --git a/extensions/aws-xray-extension/spring-boot/src/integrationTest/java/com/jashmore/sqs/extensions/xray/XrayExtensionIntegrationTest.java b/extensions/aws-xray-extension/spring-boot/src/integrationTest/java/com/jashmore/sqs/extensions/xray/XrayExtensionIntegrationTest.java index 91bec712..55d531b4 100644 --- a/extensions/aws-xray-extension/spring-boot/src/integrationTest/java/com/jashmore/sqs/extensions/xray/XrayExtensionIntegrationTest.java +++ b/extensions/aws-xray-extension/spring-boot/src/integrationTest/java/com/jashmore/sqs/extensions/xray/XrayExtensionIntegrationTest.java @@ -10,7 +10,7 @@ import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.extensions.xray.spring.SqsListenerXrayConfiguration; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.io.IOException; import java.net.DatagramPacket; diff --git a/extensions/aws-xray-extension/spring-boot/src/main/java/com/jashmore/sqs/extensions/xray/spring/SqsListenerXrayConfiguration.java b/extensions/aws-xray-extension/spring-boot/src/main/java/com/jashmore/sqs/extensions/xray/spring/SqsListenerXrayConfiguration.java index 85d9f3bf..da3eb860 100644 --- a/extensions/aws-xray-extension/spring-boot/src/main/java/com/jashmore/sqs/extensions/xray/spring/SqsListenerXrayConfiguration.java +++ b/extensions/aws-xray-extension/spring-boot/src/main/java/com/jashmore/sqs/extensions/xray/spring/SqsListenerXrayConfiguration.java @@ -2,6 +2,7 @@ import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorder; +import com.jashmore.sqs.client.DefaultSqsAsyncClientProvider; import com.jashmore.sqs.extensions.xray.client.ClientSegmentMutator; import com.jashmore.sqs.extensions.xray.client.ClientSegmentNamingStrategy; import com.jashmore.sqs.extensions.xray.client.StaticClientSegmentNamingStrategy; @@ -9,8 +10,7 @@ import com.jashmore.sqs.extensions.xray.client.XrayWrappedSqsAsyncClient; import com.jashmore.sqs.extensions.xray.decorator.BasicXrayMessageProcessingDecorator; import com.jashmore.sqs.extensions.xray.decorator.StaticDecoratorSegmentNamingStrategy; -import com.jashmore.sqs.spring.client.DefaultSqsAsyncClientProvider; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; diff --git a/extensions/aws-xray-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/xray/decorator/SqsListenerXrayConfigurationTest.java b/extensions/aws-xray-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/xray/decorator/SqsListenerXrayConfigurationTest.java index 26ef65aa..b86cba8b 100644 --- a/extensions/aws-xray-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/xray/decorator/SqsListenerXrayConfigurationTest.java +++ b/extensions/aws-xray-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/xray/decorator/SqsListenerXrayConfigurationTest.java @@ -15,7 +15,7 @@ import com.jashmore.sqs.decorator.MessageProcessingContext; import com.jashmore.sqs.extensions.xray.client.XrayWrappedSqsAsyncClient; import com.jashmore.sqs.extensions.xray.spring.SqsListenerXrayConfiguration; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; import java.util.HashMap; import org.junit.jupiter.api.AfterEach; diff --git a/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorAsynchronousIntegrationTest.java b/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorAsynchronousIntegrationTest.java index 8bcb0bb0..0828c6c2 100644 --- a/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorAsynchronousIntegrationTest.java +++ b/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorAsynchronousIntegrationTest.java @@ -14,7 +14,7 @@ import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.extensions.brave.spring.BraveMessageProcessingDecoratorConfiguration; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.HashMap; import java.util.Map; diff --git a/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorIntegrationTest.java b/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorIntegrationTest.java index e579080d..49742fb8 100644 --- a/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorIntegrationTest.java +++ b/extensions/brave-extension/spring-boot/src/test/java/com/jashmore/sqs/extensions/brave/BraveMessageProcessingDecoratorIntegrationTest.java @@ -12,7 +12,7 @@ import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.extensions.brave.spring.BraveMessageProcessingDecoratorConfiguration; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; diff --git a/extensions/spring-cloud-schema-registry-extension/avro-spring-cloud-schema-registry-extension/src/test/java/it/com/jashmore/sqs/extensions/registry/avro/AvroSpringCloudSchemaRegistryIntegrationTest.java b/extensions/spring-cloud-schema-registry-extension/avro-spring-cloud-schema-registry-extension/src/test/java/it/com/jashmore/sqs/extensions/registry/avro/AvroSpringCloudSchemaRegistryIntegrationTest.java index d92891c3..fca42c20 100644 --- a/extensions/spring-cloud-schema-registry-extension/avro-spring-cloud-schema-registry-extension/src/test/java/it/com/jashmore/sqs/extensions/registry/avro/AvroSpringCloudSchemaRegistryIntegrationTest.java +++ b/extensions/spring-cloud-schema-registry-extension/avro-spring-cloud-schema-registry-extension/src/test/java/it/com/jashmore/sqs/extensions/registry/avro/AvroSpringCloudSchemaRegistryIntegrationTest.java @@ -10,7 +10,7 @@ import com.jashmore.sqs.extensions.registry.model.Author; import com.jashmore.sqs.extensions.registry.model.Book; import com.jashmore.sqs.registry.AvroSchemaRegistrySqsAsyncClient; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; diff --git a/settings.gradle.kts b/settings.gradle.kts index 0415ef39..d9555b73 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,8 +22,10 @@ include( ":java-dynamic-sqs-listener-api", ":java-dynamic-sqs-listener-core", + // Annotations + ":java-dynamic-sqs-listener-annotations", + // Spring - ":java-dynamic-sqs-listener-spring-api", ":java-dynamic-sqs-listener-spring-core", ":java-dynamic-sqs-listener-spring-starter", @@ -75,8 +77,10 @@ include( project(":java-dynamic-sqs-listener-api").projectDir = file("api") project(":java-dynamic-sqs-listener-core").projectDir = file("core") +// Annotations +project(":java-dynamic-sqs-listener-annotations").projectDir = file("annotations") + // Spring -project(":java-dynamic-sqs-listener-spring-api").projectDir = file("spring/spring-api") project(":java-dynamic-sqs-listener-spring-core").projectDir = file("spring/spring-core") project(":java-dynamic-sqs-listener-spring-starter").projectDir = file("spring/spring-starter") diff --git a/spring/spring-api/build.gradle.kts b/spring/spring-api/build.gradle.kts deleted file mode 100644 index c3bf22f5..00000000 --- a/spring/spring-api/build.gradle.kts +++ /dev/null @@ -1,6 +0,0 @@ - -description = "API for the Spring Starter that will allow for definitions and extension of the Spring Starter" - -dependencies { - implementation(project(":java-dynamic-sqs-listener-api")) -} diff --git a/spring/spring-core/build.gradle.kts b/spring/spring-core/build.gradle.kts index 167557ae..b043503f 100644 --- a/spring/spring-core/build.gradle.kts +++ b/spring/spring-core/build.gradle.kts @@ -6,8 +6,8 @@ val springBootVersion: String by project dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion")) - api(project(":java-dynamic-sqs-listener-spring-api")) api(project(":java-dynamic-sqs-listener-core")) + api(project(":java-dynamic-sqs-listener-annotations")) implementation(project(":common-utils")) implementation(project(":annotation-utils")) compileOnly(project(":documentation-annotations")) diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageArgumentResolutionSpringIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageArgumentResolutionSpringIntegrationTest.java index 8098f4c0..54a922a8 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageArgumentResolutionSpringIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageArgumentResolutionSpringIntegrationTest.java @@ -4,7 +4,7 @@ import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageAttributeSpringIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageAttributeSpringIntegrationTest.java index de3eeaae..ede489b5 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageAttributeSpringIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageAttributeSpringIntegrationTest.java @@ -6,7 +6,7 @@ import com.jashmore.sqs.argument.attribute.MessageAttributeDataTypes; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.Collections; import java.util.concurrent.CountDownLatch; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageSystemAttributeSpringIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageSystemAttributeSpringIntegrationTest.java index dbab7e72..531c9d2b 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageSystemAttributeSpringIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/argument/MessageSystemAttributeSpringIntegrationTest.java @@ -8,7 +8,7 @@ import com.jashmore.sqs.argument.attribute.MessageSystemAttribute; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.time.OffsetDateTime; import java.util.Collections; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/client/MultipleSqsAsyncClientIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/client/MultipleSqsAsyncClientIntegrationTest.java index 73a35d2d..f918b6e1 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/client/MultipleSqsAsyncClientIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/client/MultipleSqsAsyncClientIntegrationTest.java @@ -1,10 +1,8 @@ package com.jashmore.sqs.client; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; -import com.jashmore.sqs.spring.client.DefaultSqsAsyncClientProvider; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.HashMap; import java.util.Map; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerEnvironmentIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerEnvironmentIntegrationTest.java index d32df55b..e04100a6 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerEnvironmentIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerEnvironmentIntegrationTest.java @@ -5,7 +5,7 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerIntegrationTest.java index 3451492f..a62f7c7f 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerIntegrationTest.java @@ -5,7 +5,7 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerMessageDecoratorIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerMessageDecoratorIntegrationTest.java index b2ef016b..cb54ed18 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerMessageDecoratorIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerMessageDecoratorIntegrationTest.java @@ -6,7 +6,7 @@ import com.jashmore.sqs.decorator.MessageProcessingDecorator; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerVisibilityIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerVisibilityIntegrationTest.java index 2fc01c1c..7ca866c9 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerVisibilityIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/basic/QueueListenerVisibilityIntegrationTest.java @@ -5,7 +5,7 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.ExpectedTestException; import com.jashmore.sqs.util.LocalSqsAsyncClient; import com.jashmore.sqs.util.SqsQueuesConfig; diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactoryTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/fifo/FifoMessageListenerContainerFactoryTest.java similarity index 98% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactoryTest.java rename to spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/fifo/FifoMessageListenerContainerFactoryTest.java index 516d01d7..9bb4a95e 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactoryTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/fifo/FifoMessageListenerContainerFactoryTest.java @@ -1,7 +1,8 @@ -package com.jashmore.sqs.spring.container.fifo; +package com.jashmore.sqs.container.fifo; import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.fifo.FifoQueueListener; import com.jashmore.sqs.argument.attribute.MessageSystemAttribute; import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerEnvironmentIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerEnvironmentIntegrationTest.java index d7c688b4..4b56397a 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerEnvironmentIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerEnvironmentIntegrationTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerIntegrationTest.java index 0ab906dd..776e75fd 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerIntegrationTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerMessageDecoratorIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerMessageDecoratorIntegrationTest.java index 8b440b00..97ae76bf 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerMessageDecoratorIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerMessageDecoratorIntegrationTest.java @@ -2,11 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.decorator.MessageProcessingContext; import com.jashmore.sqs.decorator.MessageProcessingDecorator; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerVisibilityIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerVisibilityIntegrationTest.java index c3d75dfb..50e58fb7 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerVisibilityIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/container/prefetch/PrefetchingQueueListenerVisibilityIntegrationTest.java @@ -2,11 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.util.ExpectedTestException; import com.jashmore.sqs.util.LocalSqsAsyncClient; import com.jashmore.sqs.util.SqsQueuesConfig; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryIntegrationTest.java index 2aab253a..1058594e 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/decorator/visibilityextender/AutoVisibilityExtenderMessageProcessingDecoratorFactoryIntegrationTest.java @@ -5,8 +5,8 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.decorator.visibilityextender.AutoVisibilityExtender; +import com.jashmore.sqs.annotations.core.basic.QueueListener; +import com.jashmore.sqs.annotations.decorator.visibilityextender.AutoVisibilityExtender; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.time.Duration; import java.util.concurrent.CountDownLatch; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/proxy/ProxyBeanQueueListenerResolutionIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/proxy/ProxyBeanQueueListenerResolutionIntegrationTest.java index cac150df..ef33af35 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/proxy/ProxyBeanQueueListenerResolutionIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/proxy/ProxyBeanQueueListenerResolutionIntegrationTest.java @@ -5,7 +5,7 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/queue/EnvironmentQueueResolverIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/queue/EnvironmentQueueResolverIntegrationTest.java index cf23239b..c1abd902 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/queue/EnvironmentQueueResolverIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/queue/EnvironmentQueueResolverIntegrationTest.java @@ -2,9 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.client.QueueResolver; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.queue.QueueResolver; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/DefaultMessageListenerContainerCoordinatorIntegrationTest.java b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/SpringMessageListenerContainerCoordinatorIntegrationTest.java similarity index 88% rename from spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/DefaultMessageListenerContainerCoordinatorIntegrationTest.java rename to spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/SpringMessageListenerContainerCoordinatorIntegrationTest.java index ea4fcca7..1e950f95 100644 --- a/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/DefaultMessageListenerContainerCoordinatorIntegrationTest.java +++ b/spring/spring-core/src/integrationTest/java/com/jashmore/sqs/service/SpringMessageListenerContainerCoordinatorIntegrationTest.java @@ -5,8 +5,8 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.MessageListenerContainerCoordinator; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -21,9 +21,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @Slf4j -@SpringBootTest(classes = { DefaultMessageListenerContainerCoordinatorIntegrationTest.TestConfig.class, QueueListenerConfiguration.class }) +@SpringBootTest(classes = { SpringMessageListenerContainerCoordinatorIntegrationTest.TestConfig.class, QueueListenerConfiguration.class }) @ExtendWith(SpringExtension.class) -public class DefaultMessageListenerContainerCoordinatorIntegrationTest { +public class SpringMessageListenerContainerCoordinatorIntegrationTest { private static final String QUEUE_NAME = "DefaultMessageListenerContainerCoordinatorIntegrationTest"; private static final int MESSAGE_VISIBILITY_IN_SECONDS = 1; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/config/QueueListenerConfiguration.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/config/QueueListenerConfiguration.java index 1081cda7..0b3f2ae5 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/config/QueueListenerConfiguration.java +++ b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/config/QueueListenerConfiguration.java @@ -1,6 +1,14 @@ package com.jashmore.sqs.spring.config; import com.fasterxml.jackson.databind.ObjectMapper; +import com.jashmore.sqs.annotations.core.basic.BasicAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.basic.QueueListenerParser; +import com.jashmore.sqs.annotations.core.fifo.FifoAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.fifo.FifoQueueListenerParser; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListenerParser; +import com.jashmore.sqs.annotations.decorator.visibilityextender.AutoVisibilityExtenderMessageProcessingDecoratorFactory; +import com.jashmore.sqs.placeholder.PlaceholderResolver; import com.jashmore.sqs.argument.ArgumentResolver; import com.jashmore.sqs.argument.ArgumentResolverService; import com.jashmore.sqs.argument.DelegatingArgumentResolverService; @@ -12,27 +20,22 @@ import com.jashmore.sqs.argument.payload.mapper.JacksonPayloadMapper; import com.jashmore.sqs.container.MessageListenerContainer; import com.jashmore.sqs.decorator.MessageProcessingDecorator; -import com.jashmore.sqs.spring.client.DefaultSqsAsyncClientProvider; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.DefaultMessageListenerContainerCoordinator; -import com.jashmore.sqs.spring.container.DefaultMessageListenerContainerCoordinatorProperties; -import com.jashmore.sqs.spring.container.MessageListenerContainerCoordinator; -import com.jashmore.sqs.spring.container.MessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.StaticDefaultMessageListenerContainerCoordinatorProperties; -import com.jashmore.sqs.spring.container.basic.BasicMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.basic.QueueListenerParser; -import com.jashmore.sqs.spring.container.fifo.FifoMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.fifo.FifoQueueListenerParser; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListenerParser; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactory; -import com.jashmore.sqs.spring.decorator.visibilityextender.AutoVisibilityExtenderMessageProcessingDecoratorFactory; +import com.jashmore.sqs.client.DefaultSqsAsyncClientProvider; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.spring.container.SpringMessageListenerContainerCoordinator; +import com.jashmore.sqs.spring.container.SpringMessageListenerContainerCoordinatorProperties; +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.spring.container.StaticSpringMessageListenerContainerCoordinatorProperties; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactory; import com.jashmore.sqs.spring.jackson.SqsListenerObjectMapperSupplier; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.DefaultQueueResolver; -import com.jashmore.sqs.spring.queue.QueueResolver; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.client.DefaultPlaceholderQueueResolver; +import com.jashmore.sqs.client.QueueResolver; import java.util.Collections; import java.util.List; + +import com.jashmore.sqs.spring.placeholder.SpringPlaceholderResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -85,7 +88,7 @@ public SqsAsyncClientProvider sqsAsyncClientProvider(final SqsAsyncClient defaul } /** - * Contains all of the configuration that needs to supplied if there is no {@link ArgumentResolverService} class defined. + * Contains all the configuration that needs to supplied if there is no {@link ArgumentResolverService} class defined. * *

Note that if a custom {@link ArgumentResolverService} is defined by the user, none of the {@link ArgumentResolver}s will be included in the context * of the application. This allows the ability of the consumer to limit the amount of code needed to resolve arguments to only the certain criteria that @@ -165,20 +168,26 @@ public MessageArgumentResolver messageArgumentResolver() { } } + @Bean + @ConditionalOnMissingBean(PlaceholderResolver.class) + public PlaceholderResolver placeholderResolver(final Environment environment) { + return new SpringPlaceholderResolver(environment); + } + /** - * The default provided {@link QueueResolver} that can be used if it not overridden by a user defined bean. + * The default provided {@link QueueResolver} that can be used if it is not overridden by a user defined bean. * - * @param environment the environment for this spring application + * @param placeholderResolver the environment for this spring application * @return the default service used for queue resolution */ @Bean @ConditionalOnMissingBean(QueueResolver.class) - public QueueResolver queueResolver(final Environment environment) { - return new DefaultQueueResolver(environment); + public QueueResolver queueResolver(final PlaceholderResolver placeholderResolver) { + return new DefaultPlaceholderQueueResolver(placeholderResolver); } /** - * Configuration used in regards to searching the application code for methods that need to be wrapped in {@link MessageListenerContainer}s. + * Configuration used with regard to searching the application code for methods that need to be wrapped in {@link MessageListenerContainer}s. * *

If the user has defined their own {@link MessageListenerContainerCoordinator} this default configuration will not be used. This allows the * consumer to configure the wrapping of their bean methods to how they feel is optimal. It can be also done if there is a bug in the existing default @@ -189,14 +198,14 @@ public QueueResolver queueResolver(final Environment environment) { public static class QueueWrappingConfiguration { /** - * The configuration properties for the {@link DefaultMessageListenerContainerCoordinator}. + * The configuration properties for the {@link SpringMessageListenerContainerCoordinator}. * * @return the default configuration properties for the coordinator */ @Bean - @ConditionalOnMissingBean(DefaultMessageListenerContainerCoordinatorProperties.class) - public DefaultMessageListenerContainerCoordinatorProperties defaultMessageListenerContainerCoordinatorProperties() { - return StaticDefaultMessageListenerContainerCoordinatorProperties.builder().isAutoStartContainersEnabled(true).build(); + @ConditionalOnMissingBean(SpringMessageListenerContainerCoordinatorProperties.class) + public SpringMessageListenerContainerCoordinatorProperties defaultMessageListenerContainerCoordinatorProperties() { + return StaticSpringMessageListenerContainerCoordinatorProperties.builder().isAutoStartContainersEnabled(true).build(); } /** @@ -211,10 +220,10 @@ public DefaultMessageListenerContainerCoordinatorProperties defaultMessageListen */ @Bean public MessageListenerContainerCoordinator messageListenerContainerCoordinator( - final DefaultMessageListenerContainerCoordinatorProperties properties, + final SpringMessageListenerContainerCoordinatorProperties properties, final List messageListenerContainerFactories ) { - return new DefaultMessageListenerContainerCoordinator(properties, messageListenerContainerFactories); + return new SpringMessageListenerContainerCoordinator(properties, messageListenerContainerFactories); } /** @@ -235,25 +244,25 @@ public DecoratingMessageProcessorFactory decoratingMessageProcessorFactory( @Bean public AutoVisibilityExtenderMessageProcessingDecoratorFactory autoVisibilityExtendMessageProcessingDecoratorFactory( - final Environment environment + final PlaceholderResolver placeholderResolver ) { - return new AutoVisibilityExtenderMessageProcessingDecoratorFactory(environment); + return new AutoVisibilityExtenderMessageProcessingDecoratorFactory(placeholderResolver); } } /** - * Contains all of the core {@link MessageListenerContainerFactory} implementations that should be enabled by default. + * Contains all the core {@link MessageListenerContainerFactory} implementations that should be enabled by default. * *

The consumer can provide any other {@link MessageListenerContainerFactory} beans in their context and these will be included in the automatic - * wrapping of the methods by the {@link #messageListenerContainerCoordinator(DefaultMessageListenerContainerCoordinatorProperties, List)} bean. + * wrapping of the methods by the {@link #messageListenerContainerCoordinator(SpringMessageListenerContainerCoordinatorProperties, List)} bean. */ @Configuration public static class MessageListenerContainerFactoryConfiguration { @Bean @ConditionalOnMissingBean(QueueListenerParser.class) - public QueueListenerParser queueListenerParser(final Environment environment) { - return new QueueListenerParser(environment); + public QueueListenerParser queueListenerParser(final PlaceholderResolver placeholderResolver) { + return new QueueListenerParser(placeholderResolver); } @Bean @@ -264,7 +273,7 @@ public MessageListenerContainerFactory basicMessageListenerContainerFactory( final QueueListenerParser queueListenerParser, final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory ) { - return new BasicMessageListenerContainerFactory( + return new BasicAnnotationMessageListenerContainerFactory( argumentResolverService, sqsAsyncClientProvider, queueResolver, @@ -275,8 +284,8 @@ public MessageListenerContainerFactory basicMessageListenerContainerFactory( @Bean @ConditionalOnMissingBean(PrefetchingQueueListenerParser.class) - public PrefetchingQueueListenerParser prefetchingQueueListenerParser(final Environment environment) { - return new PrefetchingQueueListenerParser(environment); + public PrefetchingQueueListenerParser prefetchingQueueListenerParser(final PlaceholderResolver placeholderResolver) { + return new PrefetchingQueueListenerParser(placeholderResolver); } @Bean @@ -287,7 +296,7 @@ public MessageListenerContainerFactory prefetchingMessageListenerContainerFactor final PrefetchingQueueListenerParser prefetchingQueueListenerParser, final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory ) { - return new PrefetchingMessageListenerContainerFactory( + return new PrefetchingAnnotationMessageListenerContainerFactory( argumentResolverService, sqsAsyncClientProvider, queueResolver, @@ -298,8 +307,8 @@ public MessageListenerContainerFactory prefetchingMessageListenerContainerFactor @Bean @ConditionalOnMissingBean(FifoQueueListenerParser.class) - public FifoQueueListenerParser fifoMessageListenerParser(final Environment environment) { - return new FifoQueueListenerParser(environment); + public FifoQueueListenerParser fifoMessageListenerParser(final PlaceholderResolver placeholderResolver) { + return new FifoQueueListenerParser(placeholderResolver); } @Bean @@ -310,7 +319,7 @@ public MessageListenerContainerFactory fifoMessageListenerContainerFactory( final FifoQueueListenerParser fifoQueueListenerParser, final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory ) { - return new FifoMessageListenerContainerFactory( + return new FifoAnnotationMessageListenerContainerFactory( argumentResolverService, sqsAsyncClientProvider, queueResolver, diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractAnnotationMessageListenerContainerFactory.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractAnnotationMessageListenerContainerFactory.java deleted file mode 100644 index 12ee79ee..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractAnnotationMessageListenerContainerFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.jashmore.sqs.spring.container; - -import com.jashmore.sqs.container.MessageListenerContainer; -import com.jashmore.sqs.util.annotation.AnnotationUtils; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -/** - * Abstract {@link MessageListenerContainerFactory} that will use annotations to determine whether the method can be wrapped or - * not. - * - * @param the type of annotation that the method should have for it to be wrapped - */ -public abstract class AbstractAnnotationMessageListenerContainerFactory implements MessageListenerContainerFactory { - - @Override - public boolean canHandleMethod(final Method method) { - return AnnotationUtils.findMethodAnnotation(method, getAnnotationClass()).isPresent(); - } - - @Override - public MessageListenerContainer buildContainer(final Object bean, final Method method) { - final T annotation = AnnotationUtils - .findMethodAnnotation(method, getAnnotationClass()) - .orElseThrow(() -> - new RuntimeException("Trying to wrap method that does not contain annotation: @" + getAnnotationClass().getSimpleName()) - ); - - return wrapMethodContainingAnnotation(bean, method, annotation); - } - - /** - * The class of the annotation that should be checked against. - * - * @return the class for the annotation for this queue wrapper - */ - protected abstract Class getAnnotationClass(); - - /** - * Helper method that provides the annotation found on the method with the method and bean to be wrapped. - * - * @param bean the bean instance for the corresponding method - * @param method the method that should be wrapped - * @param annotation the annotation found on the method containing details about this listener - * @return the container that wraps the method for usage by the queue listeners - */ - protected abstract MessageListenerContainer wrapMethodContainingAnnotation(final Object bean, final Method method, T annotation); -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractCoreMessageListenerContainerFactory.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractCoreMessageListenerContainerFactory.java deleted file mode 100644 index 3960129c..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/AbstractCoreMessageListenerContainerFactory.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.jashmore.sqs.spring.container; - -import com.jashmore.documentation.annotations.Nullable; -import com.jashmore.sqs.QueueProperties; -import com.jashmore.sqs.argument.ArgumentResolverService; -import com.jashmore.sqs.container.MessageListenerContainer; -import com.jashmore.sqs.processor.CoreMessageProcessor; -import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; -import com.jashmore.sqs.spring.util.IdentifierUtils; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.function.Supplier; -import org.springframework.util.StringUtils; -import software.amazon.awssdk.services.sqs.SqsAsyncClient; - -/** - * Abstract Factory for building the core {@link MessageListenerContainer}s which handles actions like calculating the identifier, - * getting the SQS client etc. - * - * @param the Spring annotation that is used to define the container's properties - * @param

the properties object that configures the {@link MessageListenerContainer} - */ -public abstract class AbstractCoreMessageListenerContainerFactory - extends AbstractAnnotationMessageListenerContainerFactory { - - private final SqsAsyncClientProvider sqsAsyncClientProvider; - private final QueueResolver queueResolver; - private final CoreAnnotationParser annotationParser; - private final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory; - private final ArgumentResolverService argumentResolverService; - - protected AbstractCoreMessageListenerContainerFactory( - final SqsAsyncClientProvider sqsAsyncClientProvider, - final QueueResolver queueResolver, - final CoreAnnotationParser annotationParser, - final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory, - final ArgumentResolverService argumentResolverService - ) { - this.sqsAsyncClientProvider = sqsAsyncClientProvider; - this.queueResolver = queueResolver; - this.annotationParser = annotationParser; - this.decoratingMessageProcessorFactory = decoratingMessageProcessorFactory; - this.argumentResolverService = argumentResolverService; - } - - /** - * Given the parsed information from the annotation, build the actual container. - * - * @param identifier the identifier that uniquely defines this container - * @param sqsAsyncClient the client that will communicate to SQS in this container - * @param queueProperties the queue properties - * @param containerProperties the specific configuration properties for the container - * @param messageProcessorSupplier the message processor supplier that will execute the message listener - * @return the constructed container from these properties - */ - protected abstract MessageListenerContainer buildContainer( - String identifier, - SqsAsyncClient sqsAsyncClient, - QueueProperties queueProperties, - P containerProperties, - Supplier messageProcessorSupplier - ); - - /** - * Obtain the optional identifier from the annotation. - * - *

If this is null or empty, the actual identifier will be constructed from the message listener's message signature. - * - * @param annotation the annotation to process - * @return the optional identifier - */ - @Nullable - protected abstract String getIdentifier(A annotation); - - /** - * Get the queue name or URL from the annotation. - * - * @param annotation the annotation to process - * @return the queue name or URL - */ - protected abstract String getQueueNameOrUrl(A annotation); - - /** - * Get the optional SQS Client identifier. - * - *

If this is null or empty, the default SQS client is used. - * - * @param annotation the annotation to process - * @return the optional client identifier - * @see SqsAsyncClientProvider for what will provide the SQS Client - */ - @Nullable - protected abstract String getSqsClientIdentifier(A annotation); - - @Override - protected MessageListenerContainer wrapMethodContainingAnnotation(final Object bean, final Method method, final A annotation) { - final SqsAsyncClient sqsAsyncClient = getSqsAsyncClient(annotation); - final QueueProperties queueProperties = getQueueProperties(sqsAsyncClient, annotation); - final P properties = annotationParser.parse(annotation); - final String identifier = IdentifierUtils.buildIdentifierForMethod(getIdentifier(annotation), bean.getClass(), method); - final Supplier messageProcessorSupplier = () -> - decoratingMessageProcessorFactory.decorateMessageProcessor( - sqsAsyncClient, - identifier, - queueProperties, - bean, - method, - new CoreMessageProcessor(argumentResolverService, queueProperties, sqsAsyncClient, method, bean) - ); - - return buildContainer(identifier, sqsAsyncClient, queueProperties, properties, messageProcessorSupplier); - } - - private SqsAsyncClient getSqsAsyncClient(A annotation) { - final String sqsClient = getSqsClientIdentifier(annotation); - - if (!StringUtils.hasText(sqsClient)) { - return sqsAsyncClientProvider - .getDefaultClient() - .orElseThrow(() -> new MessageListenerContainerInitialisationException("Expected the default SQS Client but there is none") - ); - } - - return sqsAsyncClientProvider - .getClient(sqsClient) - .orElseThrow(() -> - new MessageListenerContainerInitialisationException("Expected a client with id '" + sqsClient + "' but none were found") - ); - } - - private QueueProperties getQueueProperties(final SqsAsyncClient sqsAsyncClient, final A annotation) { - final String queueNameOrUrl = getQueueNameOrUrl(annotation); - - return QueueProperties.builder().queueUrl(queueResolver.resolveQueueUrl(sqsAsyncClient, queueNameOrUrl)).build(); - } -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/CoreAnnotationParser.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/CoreAnnotationParser.java deleted file mode 100644 index 3db43070..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/CoreAnnotationParser.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jashmore.sqs.spring.container; - -import java.lang.annotation.Annotation; - -/** - * Parser used for converting an annotation into the properties needed for one of the core {@link com.jashmore.sqs.container.MessageListenerContainer}s. - */ -public interface CoreAnnotationParser { - /** - * Parse the supplied annotation into the properties to build a {@link com.jashmore.sqs.container.MessageListenerContainer}. - * - * @param annotation the annotation to parse - * @return the container properties - */ - P parse(A annotation); -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinator.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinator.java similarity index 86% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinator.java rename to spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinator.java index f114391e..628441e8 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinator.java +++ b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinator.java @@ -17,6 +17,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Supplier; + +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.container.MessageListenerContainerFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -31,10 +34,10 @@ */ @Slf4j @ThreadSafe -public class DefaultMessageListenerContainerCoordinator +public class SpringMessageListenerContainerCoordinator implements MessageListenerContainerCoordinator, ApplicationContextAware, SmartLifecycle { - private final DefaultMessageListenerContainerCoordinatorProperties properties; + private final SpringMessageListenerContainerCoordinatorProperties properties; /** * These {@link MessageListenerContainerFactory}s should be injected by the spring application and therefore to add more wrappers into the * system a corresponding bean with this interface must be included in the application. @@ -58,8 +61,8 @@ public class DefaultMessageListenerContainerCoordinator */ private final AtomicBoolean isRunning = new AtomicBoolean(false); - public DefaultMessageListenerContainerCoordinator( - final DefaultMessageListenerContainerCoordinatorProperties properties, + public SpringMessageListenerContainerCoordinator( + final SpringMessageListenerContainerCoordinatorProperties properties, final List messageListenerContainerFactories ) { this.properties = properties; @@ -214,17 +217,17 @@ private static Map calculateMessageListenerCon final Object bean = applicationContext.getBean(beanName); for (final Method method : bean.getClass().getMethods()) { for (final MessageListenerContainerFactory annotationProcessor : messageListenerContainerFactories) { - if (annotationProcessor.canHandleMethod(method)) { - final MessageListenerContainer messageListenerContainer = annotationProcessor.buildContainer(bean, method); - if (messageContainers.containsKey(messageListenerContainer.getIdentifier())) { - throw new IllegalStateException( - "Created two MessageListenerContainers with the same identifier: " + - messageListenerContainer.getIdentifier() - ); - } - log.debug("Created MessageListenerContainer with id: {}", messageListenerContainer.getIdentifier()); - messageContainers.put(messageListenerContainer.getIdentifier(), messageListenerContainer); - } + annotationProcessor.buildContainer(bean, method) + .ifPresent(messageListenerContainer -> { + if (messageContainers.containsKey(messageListenerContainer.getIdentifier())) { + throw new IllegalStateException( + "Created two MessageListenerContainers with the same identifier: " + + messageListenerContainer.getIdentifier() + ); + } + log.debug("Created MessageListenerContainer with id: {}", messageListenerContainer.getIdentifier()); + messageContainers.put(messageListenerContainer.getIdentifier(), messageListenerContainer); + }); } } } diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorProperties.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorProperties.java similarity index 83% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorProperties.java rename to spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorProperties.java index 05337ecb..efc22611 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorProperties.java +++ b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorProperties.java @@ -2,7 +2,7 @@ import com.jashmore.sqs.container.MessageListenerContainer; -public interface DefaultMessageListenerContainerCoordinatorProperties { +public interface SpringMessageListenerContainerCoordinatorProperties { /** * Determine if the {@link MessageListenerContainer}s should be started up by default when the Spring Container starts up. * diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticDefaultMessageListenerContainerCoordinatorProperties.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticSpringMessageListenerContainerCoordinatorProperties.java similarity index 72% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticDefaultMessageListenerContainerCoordinatorProperties.java rename to spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticSpringMessageListenerContainerCoordinatorProperties.java index eae172f9..cc318a67 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticDefaultMessageListenerContainerCoordinatorProperties.java +++ b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/StaticSpringMessageListenerContainerCoordinatorProperties.java @@ -8,7 +8,7 @@ */ @Value @Builder(toBuilder = true) -public class StaticDefaultMessageListenerContainerCoordinatorProperties implements DefaultMessageListenerContainerCoordinatorProperties { +public class StaticSpringMessageListenerContainerCoordinatorProperties implements SpringMessageListenerContainerCoordinatorProperties { boolean isAutoStartContainersEnabled; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactory.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactory.java deleted file mode 100644 index d8711192..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/basic/BasicMessageListenerContainerFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.jashmore.sqs.spring.container.basic; - -import com.jashmore.sqs.QueueProperties; -import com.jashmore.sqs.argument.ArgumentResolverService; -import com.jashmore.sqs.container.MessageListenerContainer; -import com.jashmore.sqs.container.batching.BatchingMessageListenerContainer; -import com.jashmore.sqs.container.batching.BatchingMessageListenerContainerProperties; -import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.AbstractCoreMessageListenerContainerFactory; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; -import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import software.amazon.awssdk.services.sqs.SqsAsyncClient; - -/** - * {@link com.jashmore.sqs.spring.container.MessageListenerContainerFactory} that will wrap methods annotated with - * {@link QueueListener @QueueListener} with some predefined implementations of the framework. - */ -@Slf4j -public class BasicMessageListenerContainerFactory - extends AbstractCoreMessageListenerContainerFactory { - - public BasicMessageListenerContainerFactory( - final ArgumentResolverService argumentResolverService, - final SqsAsyncClientProvider sqsAsyncClientProvider, - final QueueResolver queueResolver, - final QueueListenerParser queueListenerParser, - final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory - ) { - super(sqsAsyncClientProvider, queueResolver, queueListenerParser, decoratingMessageProcessorFactory, argumentResolverService); - } - - @Override - protected Class getAnnotationClass() { - return QueueListener.class; - } - - @Override - protected MessageListenerContainer buildContainer( - final String identifier, - final SqsAsyncClient sqsAsyncClient, - final QueueProperties queueProperties, - final BatchingMessageListenerContainerProperties containerProperties, - final Supplier messageProcessorSupplier - ) { - return new BatchingMessageListenerContainer( - identifier, - queueProperties, - sqsAsyncClient, - messageProcessorSupplier, - containerProperties - ); - } - - @Override - protected String getIdentifier(final QueueListener annotation) { - return annotation.identifier(); - } - - @Override - protected String getQueueNameOrUrl(final QueueListener annotation) { - return annotation.value(); - } - - @Override - protected String getSqsClientIdentifier(final QueueListener annotation) { - return annotation.sqsClient(); - } -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactory.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactory.java deleted file mode 100644 index ca1e440e..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/fifo/FifoMessageListenerContainerFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.jashmore.sqs.spring.container.fifo; - -import com.jashmore.sqs.QueueProperties; -import com.jashmore.sqs.argument.ArgumentResolverService; -import com.jashmore.sqs.container.MessageListenerContainer; -import com.jashmore.sqs.container.fifo.FifoMessageListenerContainer; -import com.jashmore.sqs.container.fifo.FifoMessageListenerContainerProperties; -import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.AbstractCoreMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.MessageListenerContainerFactory; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; -import java.util.function.Supplier; -import software.amazon.awssdk.services.sqs.SqsAsyncClient; - -/** - * {@link MessageListenerContainerFactory} that will wrap methods annotated with {@link FifoQueueListener @FifoQueueListener} with - * a {@link FifoMessageListenerContainer} that will automatically handle processing of messages coming from a FIFO SQS Queue. - * - *

A Spring bean needs to have a method annotated with this annotation like: - * - *

- *     @FifoQueueListener(value = "test-queue.fifo", concurrencyLevel = 10, maximumMessagesInMessageGroup = 2)
- *     public void myMessageProcessor(Message message) {
- * 
- */ -public class FifoMessageListenerContainerFactory - extends AbstractCoreMessageListenerContainerFactory { - - public FifoMessageListenerContainerFactory( - final ArgumentResolverService argumentResolverService, - final SqsAsyncClientProvider sqsAsyncClientProvider, - final QueueResolver queueResolver, - final FifoQueueListenerParser annotationParser, - final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory - ) { - super(sqsAsyncClientProvider, queueResolver, annotationParser, decoratingMessageProcessorFactory, argumentResolverService); - } - - @Override - protected Class getAnnotationClass() { - return FifoQueueListener.class; - } - - @Override - protected MessageListenerContainer buildContainer( - final String identifier, - final SqsAsyncClient sqsAsyncClient, - final QueueProperties queueProperties, - final FifoMessageListenerContainerProperties containerProperties, - final Supplier messageProcessorSupplier - ) { - return new FifoMessageListenerContainer(identifier, queueProperties, sqsAsyncClient, messageProcessorSupplier, containerProperties); - } - - @Override - protected String getIdentifier(FifoQueueListener annotation) { - return annotation.identifier(); - } - - @Override - protected String getQueueNameOrUrl(FifoQueueListener annotation) { - return annotation.value(); - } - - @Override - protected String getSqsClientIdentifier(FifoQueueListener annotation) { - return annotation.sqsClient(); - } -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactory.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactory.java deleted file mode 100644 index 5bb1a276..00000000 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/container/prefetch/PrefetchingMessageListenerContainerFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.jashmore.sqs.spring.container.prefetch; - -import com.jashmore.sqs.QueueProperties; -import com.jashmore.sqs.argument.ArgumentResolverService; -import com.jashmore.sqs.container.MessageListenerContainer; -import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainer; -import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainerProperties; -import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.AbstractCoreMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.MessageListenerContainerFactory; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; -import com.jashmore.sqs.spring.queue.QueueResolver; -import java.util.function.Supplier; -import software.amazon.awssdk.services.sqs.SqsAsyncClient; - -/** - * {@link MessageListenerContainerFactory} that will wrap methods annotated with {@link PrefetchingQueueListener @PrefetchingQueueListener} with - * some predefined implementations of the framework. - */ -public class PrefetchingMessageListenerContainerFactory - extends AbstractCoreMessageListenerContainerFactory { - - public PrefetchingMessageListenerContainerFactory( - final ArgumentResolverService argumentResolverService, - final SqsAsyncClientProvider sqsAsyncClientProvider, - final QueueResolver queueResolver, - final PrefetchingQueueListenerParser annotationParser, - final DecoratingMessageProcessorFactory decoratingMessageProcessorFactory - ) { - super(sqsAsyncClientProvider, queueResolver, annotationParser, decoratingMessageProcessorFactory, argumentResolverService); - } - - @Override - protected Class getAnnotationClass() { - return PrefetchingQueueListener.class; - } - - @Override - protected MessageListenerContainer buildContainer( - final String identifier, - final SqsAsyncClient sqsAsyncClient, - final QueueProperties queueProperties, - final PrefetchingMessageListenerContainerProperties containerProperties, - final Supplier messageProcessorSupplier - ) { - return new PrefetchingMessageListenerContainer( - identifier, - queueProperties, - sqsAsyncClient, - messageProcessorSupplier, - containerProperties - ); - } - - @Override - protected String getIdentifier(PrefetchingQueueListener annotation) { - return annotation.identifier(); - } - - @Override - protected String getQueueNameOrUrl(PrefetchingQueueListener annotation) { - return annotation.value(); - } - - @Override - protected String getSqsClientIdentifier(PrefetchingQueueListener annotation) { - return annotation.sqsClient(); - } -} diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolver.java b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolver.java new file mode 100644 index 00000000..be16ce34 --- /dev/null +++ b/spring/spring-core/src/main/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolver.java @@ -0,0 +1,18 @@ +package com.jashmore.sqs.spring.placeholder; + +import com.jashmore.sqs.placeholder.PlaceholderResolver; +import org.springframework.core.env.Environment; + +public class SpringPlaceholderResolver implements PlaceholderResolver { + + private final Environment environment; + + public SpringPlaceholderResolver(final Environment environment) { + this.environment = environment; + } + + @Override + public String resolvePlaceholders(final String text) { + return environment.resolveRequiredPlaceholders(text); + } +} diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/config/QueueListenerConfigurationTest.java b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/config/QueueListenerConfigurationTest.java index e74c0c5d..685943f5 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/config/QueueListenerConfigurationTest.java +++ b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/config/QueueListenerConfigurationTest.java @@ -9,6 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.jashmore.sqs.QueueProperties; +import com.jashmore.sqs.annotations.core.basic.BasicAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.fifo.FifoAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.fifo.FifoQueueListener; +import com.jashmore.sqs.annotations.core.fifo.FifoQueueListenerParser; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingAnnotationMessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListenerParser; import com.jashmore.sqs.argument.ArgumentResolver; import com.jashmore.sqs.argument.ArgumentResolverService; import com.jashmore.sqs.argument.DelegatingArgumentResolverService; @@ -22,29 +29,24 @@ import com.jashmore.sqs.container.prefetching.PrefetchingMessageListenerContainerProperties; import com.jashmore.sqs.decorator.MessageProcessingDecorator; import com.jashmore.sqs.processor.MessageProcessor; -import com.jashmore.sqs.spring.client.SqsAsyncClientProvider; -import com.jashmore.sqs.spring.container.DefaultMessageListenerContainerCoordinator; -import com.jashmore.sqs.spring.container.DefaultMessageListenerContainerCoordinatorProperties; -import com.jashmore.sqs.spring.container.MessageListenerContainerCoordinator; -import com.jashmore.sqs.spring.container.MessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.basic.BasicMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.basic.QueueListener; -import com.jashmore.sqs.spring.container.basic.QueueListenerParser; -import com.jashmore.sqs.spring.container.fifo.FifoMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.fifo.FifoQueueListener; -import com.jashmore.sqs.spring.container.fifo.FifoQueueListenerParser; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingMessageListenerContainerFactory; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListenerParser; -import com.jashmore.sqs.spring.decorator.MessageProcessingDecoratorFactory; +import com.jashmore.sqs.client.SqsAsyncClientProvider; +import com.jashmore.sqs.spring.container.SpringMessageListenerContainerCoordinator; +import com.jashmore.sqs.spring.container.SpringMessageListenerContainerCoordinatorProperties; +import com.jashmore.sqs.container.MessageListenerContainerCoordinator; +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import com.jashmore.sqs.annotations.core.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListenerParser; +import com.jashmore.sqs.decorator.MessageProcessingDecoratorFactory; import com.jashmore.sqs.spring.jackson.SqsListenerObjectMapperSupplier; -import com.jashmore.sqs.spring.processor.DecoratingMessageProcessorFactory; +import com.jashmore.sqs.processor.DecoratingMessageProcessorFactory; import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; + +import com.jashmore.sqs.spring.placeholder.SpringPlaceholderResolver; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -343,7 +345,7 @@ void defaultMessageListenerContainerCoordinatorIsUsedIfUserContextDoesNotDefineI .run(context -> { assertThat(context).hasSingleBean(MessageListenerContainerCoordinator.class); assertThat(context.getBean(MessageListenerContainerCoordinator.class)) - .isInstanceOf(DefaultMessageListenerContainerCoordinator.class); + .isInstanceOf(SpringMessageListenerContainerCoordinator.class); }); } @@ -352,8 +354,8 @@ void defaultCoordinatorPropertiesWillAutoStartAllContainers() { contextRunner .withUserConfiguration(UserConfigurationWithSqsClient.class) .run(context -> { - final DefaultMessageListenerContainerCoordinatorProperties properties = context.getBean( - DefaultMessageListenerContainerCoordinatorProperties.class + final SpringMessageListenerContainerCoordinatorProperties properties = context.getBean( + SpringMessageListenerContainerCoordinatorProperties.class ); assertThat(properties.isAutoStartContainersEnabled()).isTrue(); }); @@ -361,19 +363,19 @@ void defaultCoordinatorPropertiesWillAutoStartAllContainers() { @Test void customDefaultCoordinatorPropertiesWillOverrideDefault() { - final DefaultMessageListenerContainerCoordinatorProperties customProperties = mock( - DefaultMessageListenerContainerCoordinatorProperties.class + final SpringMessageListenerContainerCoordinatorProperties customProperties = mock( + SpringMessageListenerContainerCoordinatorProperties.class ); when(customProperties.isAutoStartContainersEnabled()).thenReturn(false); contextRunner .withUserConfiguration(UserConfigurationWithSqsClient.class) - .withBean(DefaultMessageListenerContainerCoordinatorProperties.class, () -> customProperties) + .withBean(SpringMessageListenerContainerCoordinatorProperties.class, () -> customProperties) .run(context -> { - final DefaultMessageListenerContainerCoordinatorProperties properties = context.getBean( - DefaultMessageListenerContainerCoordinatorProperties.class + final SpringMessageListenerContainerCoordinatorProperties properties = context.getBean( + SpringMessageListenerContainerCoordinatorProperties.class ); assertThat(properties).isSameAs(customProperties); - assertThat(context.getBean(DefaultMessageListenerContainerCoordinator.class).isAutoStartup()).isFalse(); + assertThat(context.getBean(SpringMessageListenerContainerCoordinator.class).isAutoStartup()).isFalse(); }); } @@ -391,9 +393,9 @@ void allCoreDefinedMessageListenerContainerFactoriesAreIncludedInContext() { assertThat(MessageListenerContainerFactoryClasses) .containsExactlyInAnyOrder( - BasicMessageListenerContainerFactory.class, - PrefetchingMessageListenerContainerFactory.class, - FifoMessageListenerContainerFactory.class + BasicAnnotationMessageListenerContainerFactory.class, + PrefetchingAnnotationMessageListenerContainerFactory.class, + FifoAnnotationMessageListenerContainerFactory.class ); }); } @@ -406,11 +408,11 @@ void allDefinedMessageListenerContainerFactoriesAreIncludedInDefaultMessageListe final Collection messageListenerContainerFactories = context .getBeansOfType(MessageListenerContainerFactory.class) .values(); - final DefaultMessageListenerContainerCoordinator service = (DefaultMessageListenerContainerCoordinator) context.getBean( + final SpringMessageListenerContainerCoordinator service = (SpringMessageListenerContainerCoordinator) context.getBean( MessageListenerContainerCoordinator.class ); final Field argumentResolversField = - DefaultMessageListenerContainerCoordinator.class.getDeclaredField("messageListenerContainerFactories"); + SpringMessageListenerContainerCoordinator.class.getDeclaredField("messageListenerContainerFactories"); argumentResolversField.setAccessible(true); assertThat(((List) argumentResolversField.get(service))) .containsExactlyElementsOf(messageListenerContainerFactories); @@ -425,11 +427,11 @@ void userDefinedMessageListenerContainerFactoryIsIncludedInDefaultMessageListene final Collection messageListenerContainerFactories = context .getBeansOfType(MessageListenerContainerFactory.class) .values(); - final DefaultMessageListenerContainerCoordinator service = (DefaultMessageListenerContainerCoordinator) context.getBean( + final SpringMessageListenerContainerCoordinator service = (SpringMessageListenerContainerCoordinator) context.getBean( MessageListenerContainerCoordinator.class ); final Field argumentResolversField = - DefaultMessageListenerContainerCoordinator.class.getDeclaredField("messageListenerContainerFactories"); + SpringMessageListenerContainerCoordinator.class.getDeclaredField("messageListenerContainerFactories"); argumentResolversField.setAccessible(true); assertThat(((List) argumentResolversField.get(service))) .containsExactlyElementsOf(messageListenerContainerFactories); @@ -626,7 +628,7 @@ public void queueListener() {} public static class CustomPrefetchingQueueListenerParser extends PrefetchingQueueListenerParser { public CustomPrefetchingQueueListenerParser(Environment environment) { - super(environment); + super(new SpringPlaceholderResolver(environment)); } @Override @@ -638,7 +640,7 @@ protected Supplier concurrencySupplier(PrefetchingQueueListener annotat public static class CustomFifoQueueListenerParser extends FifoQueueListenerParser { public CustomFifoQueueListenerParser(Environment environment) { - super(environment); + super(new SpringPlaceholderResolver(environment)); } @Override @@ -650,7 +652,7 @@ protected Supplier concurrencySupplier(FifoQueueListener annotation) { public static class CustomQueueListenerParser extends QueueListenerParser { public CustomQueueListenerParser(Environment environment) { - super(environment); + super(new SpringPlaceholderResolver(environment)); } @Override diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorTest.java b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorTest.java deleted file mode 100644 index 0a13732f..00000000 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/DefaultMessageListenerContainerCoordinatorTest.java +++ /dev/null @@ -1,389 +0,0 @@ -package com.jashmore.sqs.spring.container; - -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.jashmore.sqs.container.MessageListenerContainer; -import java.lang.reflect.Method; -import java.util.Collections; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.context.ApplicationContext; - -@Slf4j -@ExtendWith(MockitoExtension.class) -class DefaultMessageListenerContainerCoordinatorTest { - - @Mock - private DefaultMessageListenerContainerCoordinatorProperties properties; - - @Mock - private ApplicationContext applicationContext; - - @Test - void whenNoMessageListenerContainerFactoriesPresentBeansAreNotProcessed() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - - // act - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).isEmpty(); - verify(applicationContext, never()).getBeanDefinitionNames(); - } - - @Test - void settingApplicationContextTwiceDoesNothing() { - // arrange - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] {}); - - // act - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).isEmpty(); - verify(applicationContext, times(1)).getBeanDefinitionNames(); - } - - @Test - void buildsMessageListenContainersForEachEligibleBeanMethod() throws NoSuchMethodException { - // arrange - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(messageListenerContainerFactory.canHandleMethod(method)).thenReturn(true); - final MessageListenerContainer container = mock(MessageListenerContainer.class); - when(container.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, method)).thenReturn(container); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - - // act - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).containsOnly(container); - } - - @Test - void methodsThatAreNotEligibleForWrappingWillNotCreateMessageListeners() throws NoSuchMethodException { - // arrange - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - - // act - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // assert - verify(messageListenerContainerFactory, never()).buildContainer(bean, method); - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).isEmpty(); - } - - @Test - void duplicateMessageListenerContainsThrowsExceptionWhenStarting() throws NoSuchMethodException { - // arrange - final BeanWithTwoMethods bean = new BeanWithTwoMethods(); - final Method methodOne = bean.getClass().getMethod("methodOne"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - - when(messageListenerContainerFactory.canHandleMethod(methodOne)).thenReturn(true); - final MessageListenerContainer containerOne = mock(MessageListenerContainer.class); - when(containerOne.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, methodOne)).thenReturn(containerOne); - - final Method methodTwo = bean.getClass().getMethod("methodTwo"); - when(messageListenerContainerFactory.canHandleMethod(methodTwo)).thenReturn(true); - final MessageListenerContainer containerTwo = mock(MessageListenerContainer.class); - when(containerTwo.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, methodTwo)).thenReturn(containerTwo); - - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - assertThrows(IllegalStateException.class, defaultMessageListenerContainerCoordinator::startAllContainers); - } - - @Test - void startingContainersWillStartAllMessageListenerContainersBuilt() throws NoSuchMethodException { - // arrange - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(messageListenerContainerFactory.canHandleMethod(method)).thenReturn(true); - final MessageListenerContainer container = mock(MessageListenerContainer.class); - when(container.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, method)).thenReturn(container); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).hasSize(1); - - // act - defaultMessageListenerContainerCoordinator.startAllContainers(); - - // assert - verify(container).start(); - } - - @Test - void stoppingAllContainersWillStopAllMessageListenerContainersBuilt() throws NoSuchMethodException { - // arrange - log.debug("Starting stoppingAllContainersWillStopAllMessageListenerContainersBuilt"); - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(messageListenerContainerFactory.canHandleMethod(method)).thenReturn(true); - final MessageListenerContainer container = mock(MessageListenerContainer.class); - when(container.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, method)).thenReturn(container); - doAnswer(invocationOnMock -> { - log.info("Stopping container"); - return null; - }) - .when(container) - .stop(); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - assertThat(defaultMessageListenerContainerCoordinator.getContainers()).hasSize(1); - - // act - defaultMessageListenerContainerCoordinator.stopAllContainers(); - log.info("Should have stopped all containers"); - - // assert - verify(container).stop(); - } - - @Test - void stoppingContainerThatDoesNotExistThrowsIllegalArgumentException() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - assertThrows(IllegalArgumentException.class, () -> defaultMessageListenerContainerCoordinator.stopContainer("unknown")); - } - - @Test - void stoppingIndividualContainerWithIdentifierCallsStopOnContainer() throws NoSuchMethodException { - // arrange - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(messageListenerContainerFactory.canHandleMethod(method)).thenReturn(true); - final MessageListenerContainer container = mock(MessageListenerContainer.class); - when(container.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, method)).thenReturn(container); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - defaultMessageListenerContainerCoordinator.stopContainer("identifier"); - - // assert - verify(container).stop(); - } - - @Test - void startingContainerThatDoesNotExistThrowsIllegalArgumentException() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - assertThrows(IllegalArgumentException.class, () -> defaultMessageListenerContainerCoordinator.startContainer("unknown")); - } - - @Test - void startingIndividualContainerWithIdentifierCallsStopOnContainer() throws NoSuchMethodException { - // arrange - final Bean bean = new Bean(); - final Method method = bean.getClass().getMethod("method"); - final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); - when(messageListenerContainerFactory.canHandleMethod(any(Method.class))).thenReturn(false); - when(messageListenerContainerFactory.canHandleMethod(method)).thenReturn(true); - final MessageListenerContainer container = mock(MessageListenerContainer.class); - when(container.getIdentifier()).thenReturn("identifier"); - when(messageListenerContainerFactory.buildContainer(bean, method)).thenReturn(container); - when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); - when(applicationContext.getBean("bean")).thenReturn(bean); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - defaultMessageListenerContainerCoordinator.startContainer("identifier"); - - // assert - verify(container).start(); - } - - @Test - void configuredPropertiesWillDetermineIfContainerIsAutostartup() { - // arrange - when(properties.isAutoStartContainersEnabled()).thenReturn(true).thenReturn(false); - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.isAutoStartup()).isTrue(); - assertThat(defaultMessageListenerContainerCoordinator.isAutoStartup()).isFalse(); - } - - @Test - void startLifeCycleStartsAllContainers() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = spy( - new DefaultMessageListenerContainerCoordinator(properties, emptyList()) - ); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - defaultMessageListenerContainerCoordinator.start(); - - // assert - verify(defaultMessageListenerContainerCoordinator).startAllContainers(); - } - - @Test - void stopLifeCycleStopsAllContainers() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = spy( - new DefaultMessageListenerContainerCoordinator(properties, emptyList()) - ); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - - // act - defaultMessageListenerContainerCoordinator.stop(); - - // assert - verify(defaultMessageListenerContainerCoordinator).stopAllContainers(); - } - - @Test - void stopLifeCycleWithCallbackStartsAllContainersAndRunsCallback() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = spy( - new DefaultMessageListenerContainerCoordinator(properties, emptyList()) - ); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - final Runnable callback = mock(Runnable.class); - - // act - defaultMessageListenerContainerCoordinator.stop(callback); - - // assert - verify(defaultMessageListenerContainerCoordinator).stop(); - verify(callback).run(); - } - - @Test - void beanIsNotRunningWhenStartIsNotCalled() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.isRunning()).isFalse(); - } - - @Test - void startLifeCycleSetsBeanAsRunning() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - assertThat(defaultMessageListenerContainerCoordinator.isRunning()).isFalse(); - - // act - defaultMessageListenerContainerCoordinator.start(); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.isRunning()).isTrue(); - } - - @Test - void stopLifeCycleSetsBeanAsNotRunning() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - defaultMessageListenerContainerCoordinator.setApplicationContext(applicationContext); - assertThat(defaultMessageListenerContainerCoordinator.isRunning()).isFalse(); - defaultMessageListenerContainerCoordinator.start(); - - // act - defaultMessageListenerContainerCoordinator.stop(); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.isRunning()).isFalse(); - } - - @Test - void beanShouldBeStartedLast() { - // arrange - final DefaultMessageListenerContainerCoordinator defaultMessageListenerContainerCoordinator = - new DefaultMessageListenerContainerCoordinator(properties, emptyList()); - - // assert - assertThat(defaultMessageListenerContainerCoordinator.getPhase()).isEqualTo(Integer.MAX_VALUE); - } - - @SuppressWarnings("WeakerAccess") - public static class Bean { - - public void method() {} - } - - @SuppressWarnings("WeakerAccess") - public static class BeanWithTwoMethods { - - public void methodOne() {} - - public void methodTwo() {} - } -} diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorTest.java b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorTest.java new file mode 100644 index 00000000..6e264b74 --- /dev/null +++ b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/container/SpringMessageListenerContainerCoordinatorTest.java @@ -0,0 +1,385 @@ +package com.jashmore.sqs.spring.container; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.jashmore.sqs.container.MessageListenerContainer; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Optional; + +import com.jashmore.sqs.container.MessageListenerContainerFactory; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationContext; + +@Slf4j +@ExtendWith(MockitoExtension.class) +class SpringMessageListenerContainerCoordinatorTest { + + @Mock + private SpringMessageListenerContainerCoordinatorProperties properties; + + @Mock + private ApplicationContext applicationContext; + + @Test + void whenNoMessageListenerContainerFactoriesPresentBeansAreNotProcessed() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + + // act + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // assert + assertThat(springMessageListenerContainerCoordinator.getContainers()).isEmpty(); + verify(applicationContext, never()).getBeanDefinitionNames(); + } + + @Test + void settingApplicationContextTwiceDoesNothing() { + // arrange + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] {}); + + // act + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // assert + assertThat(springMessageListenerContainerCoordinator.getContainers()).isEmpty(); + verify(applicationContext, times(1)).getBeanDefinitionNames(); + } + + @Test + void buildsMessageListenContainersForEachEligibleBeanMethod() throws NoSuchMethodException { + // arrange + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + final MessageListenerContainer container = mock(MessageListenerContainer.class); + when(container.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(method))).thenReturn(Optional.of(container)); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + + // act + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // assert + assertThat(springMessageListenerContainerCoordinator.getContainers()).containsOnly(container); + } + + @Test + void methodsThatAreNotEligibleForWrappingWillNotCreateMessageListeners() throws NoSuchMethodException { + // arrange + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + + // act + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // assert + verify(messageListenerContainerFactory, never()).buildContainer(bean, method); + assertThat(springMessageListenerContainerCoordinator.getContainers()).isEmpty(); + } + + @Test + void duplicateMessageListenerContainsThrowsExceptionWhenStarting() throws NoSuchMethodException { + // arrange + final BeanWithTwoMethods bean = new BeanWithTwoMethods(); + final Method methodOne = bean.getClass().getMethod("methodOne"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + + final MessageListenerContainer containerOne = mock(MessageListenerContainer.class); + when(containerOne.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(methodOne))).thenReturn(Optional.of(containerOne)); + + final Method methodTwo = bean.getClass().getMethod("methodTwo"); + final MessageListenerContainer containerTwo = mock(MessageListenerContainer.class); + when(containerTwo.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(methodTwo))).thenReturn(Optional.of(containerTwo)); + + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + assertThrows(IllegalStateException.class, springMessageListenerContainerCoordinator::startAllContainers); + } + + @Test + void startingContainersWillStartAllMessageListenerContainersBuilt() throws NoSuchMethodException { + // arrange + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + final MessageListenerContainer container = mock(MessageListenerContainer.class); + when(container.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(method))).thenReturn(Optional.of(container)); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + assertThat(springMessageListenerContainerCoordinator.getContainers()).hasSize(1); + + // act + springMessageListenerContainerCoordinator.startAllContainers(); + + // assert + verify(container).start(); + } + + @Test + void stoppingAllContainersWillStopAllMessageListenerContainersBuilt() throws NoSuchMethodException { + // arrange + log.debug("Starting stoppingAllContainersWillStopAllMessageListenerContainersBuilt"); + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + final MessageListenerContainer container = mock(MessageListenerContainer.class); + when(container.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(method))).thenReturn(Optional.of(container)); + doAnswer(invocationOnMock -> { + log.info("Stopping container"); + return null; + }) + .when(container) + .stop(); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + assertThat(springMessageListenerContainerCoordinator.getContainers()).hasSize(1); + + // act + springMessageListenerContainerCoordinator.stopAllContainers(); + log.info("Should have stopped all containers"); + + // assert + verify(container).stop(); + } + + @Test + void stoppingContainerThatDoesNotExistThrowsIllegalArgumentException() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + assertThrows(IllegalArgumentException.class, () -> springMessageListenerContainerCoordinator.stopContainer("unknown")); + } + + @Test + void stoppingIndividualContainerWithIdentifierCallsStopOnContainer() throws NoSuchMethodException { + // arrange + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + final MessageListenerContainer container = mock(MessageListenerContainer.class); + when(container.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(method))).thenReturn(Optional.of(container)); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + springMessageListenerContainerCoordinator.stopContainer("identifier"); + + // assert + verify(container).stop(); + } + + @Test + void startingContainerThatDoesNotExistThrowsIllegalArgumentException() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + assertThrows(IllegalArgumentException.class, () -> springMessageListenerContainerCoordinator.startContainer("unknown")); + } + + @Test + void startingIndividualContainerWithIdentifierCallsStopOnContainer() throws NoSuchMethodException { + // arrange + final Bean bean = new Bean(); + final Method method = bean.getClass().getMethod("method"); + final MessageListenerContainerFactory messageListenerContainerFactory = mock(MessageListenerContainerFactory.class); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, Collections.singletonList(messageListenerContainerFactory)); + when(messageListenerContainerFactory.buildContainer(any(Object.class), any(Method.class))).thenReturn(Optional.empty()); + final MessageListenerContainer container = mock(MessageListenerContainer.class); + when(container.getIdentifier()).thenReturn("identifier"); + when(messageListenerContainerFactory.buildContainer(eq(bean), eq(method))).thenReturn(Optional.of(container)); + when(applicationContext.getBeanDefinitionNames()).thenReturn(new String[] { "bean" }); + when(applicationContext.getBean("bean")).thenReturn(bean); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + springMessageListenerContainerCoordinator.startContainer("identifier"); + + // assert + verify(container).start(); + } + + @Test + void configuredPropertiesWillDetermineIfContainerIsAutostartup() { + // arrange + when(properties.isAutoStartContainersEnabled()).thenReturn(true).thenReturn(false); + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + + // assert + assertThat(springMessageListenerContainerCoordinator.isAutoStartup()).isTrue(); + assertThat(springMessageListenerContainerCoordinator.isAutoStartup()).isFalse(); + } + + @Test + void startLifeCycleStartsAllContainers() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = spy( + new SpringMessageListenerContainerCoordinator(properties, emptyList()) + ); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + springMessageListenerContainerCoordinator.start(); + + // assert + verify(springMessageListenerContainerCoordinator).startAllContainers(); + } + + @Test + void stopLifeCycleStopsAllContainers() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = spy( + new SpringMessageListenerContainerCoordinator(properties, emptyList()) + ); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + + // act + springMessageListenerContainerCoordinator.stop(); + + // assert + verify(springMessageListenerContainerCoordinator).stopAllContainers(); + } + + @Test + void stopLifeCycleWithCallbackStartsAllContainersAndRunsCallback() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = spy( + new SpringMessageListenerContainerCoordinator(properties, emptyList()) + ); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + final Runnable callback = mock(Runnable.class); + + // act + springMessageListenerContainerCoordinator.stop(callback); + + // assert + verify(springMessageListenerContainerCoordinator).stop(); + verify(callback).run(); + } + + @Test + void beanIsNotRunningWhenStartIsNotCalled() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + + // assert + assertThat(springMessageListenerContainerCoordinator.isRunning()).isFalse(); + } + + @Test + void startLifeCycleSetsBeanAsRunning() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + assertThat(springMessageListenerContainerCoordinator.isRunning()).isFalse(); + + // act + springMessageListenerContainerCoordinator.start(); + + // assert + assertThat(springMessageListenerContainerCoordinator.isRunning()).isTrue(); + } + + @Test + void stopLifeCycleSetsBeanAsNotRunning() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + springMessageListenerContainerCoordinator.setApplicationContext(applicationContext); + assertThat(springMessageListenerContainerCoordinator.isRunning()).isFalse(); + springMessageListenerContainerCoordinator.start(); + + // act + springMessageListenerContainerCoordinator.stop(); + + // assert + assertThat(springMessageListenerContainerCoordinator.isRunning()).isFalse(); + } + + @Test + void beanShouldBeStartedLast() { + // arrange + final SpringMessageListenerContainerCoordinator springMessageListenerContainerCoordinator = + new SpringMessageListenerContainerCoordinator(properties, emptyList()); + + // assert + assertThat(springMessageListenerContainerCoordinator.getPhase()).isEqualTo(Integer.MAX_VALUE); + } + + @SuppressWarnings("WeakerAccess") + public static class Bean { + + public void method() {} + } + + @SuppressWarnings("WeakerAccess") + public static class BeanWithTwoMethods { + + public void methodOne() {} + + public void methodTwo() {} + } +} diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolverTest.java b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolverTest.java new file mode 100644 index 00000000..e51fba21 --- /dev/null +++ b/spring/spring-core/src/test/java/com/jashmore/sqs/spring/placeholder/SpringPlaceholderResolverTest.java @@ -0,0 +1,30 @@ +package com.jashmore.sqs.spring.placeholder; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class SpringPlaceholderResolverTest { + MockEnvironment environment; + + SpringPlaceholderResolver resolver; + @BeforeEach + void setUp() { + environment = new MockEnvironment(); + resolver = new SpringPlaceholderResolver(environment); + } + + @Test + void willResolvePlaceholdersFromMapping() { + environment.withProperty("key", "value"); + assertThat(resolver.resolvePlaceholders("something ${key}")).isEqualTo("something value"); + } + + @Test + void willThrowExceptionsIfNoMappingFound() { + assertThrows(IllegalArgumentException.class, () -> resolver.resolvePlaceholders("something ${key}")); + } +} \ No newline at end of file diff --git a/spring/spring-starter/src/test/java/com/jashmore/sqs/SpringObjectMapperIntegrationTest.java b/spring/spring-starter/src/test/java/com/jashmore/sqs/SpringObjectMapperIntegrationTest.java index 8b27afa2..e187c605 100644 --- a/spring/spring-starter/src/test/java/com/jashmore/sqs/SpringObjectMapperIntegrationTest.java +++ b/spring/spring-starter/src/test/java/com/jashmore/sqs/SpringObjectMapperIntegrationTest.java @@ -8,7 +8,7 @@ import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.basic.QueueListener; +import com.jashmore.sqs.annotations.core.basic.QueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.HashMap; import java.util.Map; diff --git a/spring/spring-starter/src/test/java/com/jashmore/sqs/SqsSpringStarterIntegrationTest.java b/spring/spring-starter/src/test/java/com/jashmore/sqs/SqsSpringStarterIntegrationTest.java index 65a0403e..d85c9654 100644 --- a/spring/spring-starter/src/test/java/com/jashmore/sqs/SqsSpringStarterIntegrationTest.java +++ b/spring/spring-starter/src/test/java/com/jashmore/sqs/SqsSpringStarterIntegrationTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.jashmore.sqs.annotations.core.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.argument.payload.Payload; import com.jashmore.sqs.elasticmq.ElasticMqSqsAsyncClient; import com.jashmore.sqs.spring.config.QueueListenerConfiguration; -import com.jashmore.sqs.spring.container.prefetch.PrefetchingQueueListener; import com.jashmore.sqs.util.LocalSqsAsyncClient; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; diff --git a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/util/IdentifierUtils.java b/util/common-utils/src/main/java/com/jashmore/sqs/util/identifier/IdentifierUtils.java similarity index 66% rename from spring/spring-core/src/main/java/com/jashmore/sqs/spring/util/IdentifierUtils.java rename to util/common-utils/src/main/java/com/jashmore/sqs/util/identifier/IdentifierUtils.java index 66214c04..56cd01fc 100644 --- a/spring/spring-core/src/main/java/com/jashmore/sqs/spring/util/IdentifierUtils.java +++ b/util/common-utils/src/main/java/com/jashmore/sqs/util/identifier/IdentifierUtils.java @@ -1,14 +1,13 @@ -package com.jashmore.sqs.spring.util; - -import static com.jashmore.sqs.util.string.StringUtils.toLowerHyphenCase; +package com.jashmore.sqs.util.identifier; import java.lang.reflect.Method; + +import com.jashmore.documentation.annotations.Nullable; +import com.jashmore.sqs.util.string.StringUtils; import lombok.experimental.UtilityClass; -import org.springframework.util.StringUtils; @UtilityClass public class IdentifierUtils { - /** * Builds an identifier from a provided identifier if it is not empty, otherwise build an identifier from the class and method. * @@ -17,9 +16,9 @@ public class IdentifierUtils { * @param method the method used for the message listener, used if no identifier supplied * @return an identifier for this class' method */ - public String buildIdentifierForMethod(final String identifier, final Class clazz, final Method method) { + public String buildIdentifierForMethod(@Nullable final String identifier, final Class clazz, final Method method) { if (!StringUtils.hasText(identifier.trim())) { - return toLowerHyphenCase(clazz.getSimpleName() + "-" + method.getName()); + return StringUtils.toLowerHyphenCase(clazz.getSimpleName() + "-" + method.getName()); } else { return identifier.trim(); } diff --git a/util/common-utils/src/main/java/com/jashmore/sqs/util/string/StringUtils.java b/util/common-utils/src/main/java/com/jashmore/sqs/util/string/StringUtils.java index ea6bac69..6de09a9c 100644 --- a/util/common-utils/src/main/java/com/jashmore/sqs/util/string/StringUtils.java +++ b/util/common-utils/src/main/java/com/jashmore/sqs/util/string/StringUtils.java @@ -1,5 +1,6 @@ package com.jashmore.sqs.util.string; +import com.jashmore.documentation.annotations.Nullable; import lombok.experimental.UtilityClass; @UtilityClass @@ -59,4 +60,14 @@ public String toLowerHyphenCase(final String identifier) { return stringBuilder.toString(); } + + /** + * Return whether the text is null or empty. + * + * @param text to check + * @return whether it has content + */ + public boolean hasText(@Nullable final String text) { + return text != null && !text.isEmpty(); + } } diff --git a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/util/IdentifierUtilsTest.java b/util/common-utils/src/test/java/com/jashmore/sqs/util/identifier/IdentifierUtilsTest.java similarity index 96% rename from spring/spring-core/src/test/java/com/jashmore/sqs/spring/util/IdentifierUtilsTest.java rename to util/common-utils/src/test/java/com/jashmore/sqs/util/identifier/IdentifierUtilsTest.java index 12bfe7a7..6619d1a2 100644 --- a/spring/spring-core/src/test/java/com/jashmore/sqs/spring/util/IdentifierUtilsTest.java +++ b/util/common-utils/src/test/java/com/jashmore/sqs/util/identifier/IdentifierUtilsTest.java @@ -1,8 +1,9 @@ -package com.jashmore.sqs.spring.util; +package com.jashmore.sqs.util.identifier; import static org.assertj.core.api.Assertions.assertThat; import java.lang.reflect.Method; + import org.junit.jupiter.api.Test; class IdentifierUtilsTest { diff --git a/util/common-utils/src/test/java/com/jashmore/sqs/util/string/StringUtilsTest.java b/util/common-utils/src/test/java/com/jashmore/sqs/util/string/StringUtilsTest.java index 3f5eb3ee..8e9471e0 100644 --- a/util/common-utils/src/test/java/com/jashmore/sqs/util/string/StringUtilsTest.java +++ b/util/common-utils/src/test/java/com/jashmore/sqs/util/string/StringUtilsTest.java @@ -40,4 +40,22 @@ void nonUppercaseStartWillCreateMethodCorrectly() { assertThat(StringUtils.toLowerHyphenCase("myMethodName")).isEqualTo("my-method-name"); } } + + @Nested + class HasText { + @Test + void willReturnFalseWhenNull() { + assertThat(StringUtils.hasText(null)).isFalse(); + } + + @Test + void willReturnFalseWhenEmptyString() { + assertThat(StringUtils.hasText("")).isFalse(); + } + + @Test + void willReturnTrueWhenStringHasContent() { + assertThat(StringUtils.hasText("Hello World")).isTrue(); + } + } }