From af4dfd9a811c81b2d3d0d826cc8a199e6fd614cd Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Fri, 19 Jan 2024 17:44:24 +0100 Subject: [PATCH] Enable sqs guide (#1419) * update sqs guide * disable groovy guide * remove @NonNull annotation in kotlin class * configuration note * Change LOGGER to LOG as in all other guides Change LOGGER to LOG * Remove unused imports * Add license * Whitespace whitespace * Kotlinisation * Remove SQSClient factory from guide * Add pullout keys for kotlin class --------- Co-authored-by: Tim Yates --- .../example/micronaut/DemoConsumer.groovy | 10 +-- .../example/micronaut/SqsClientFactory.groovy | 42 ---------- .../micronaut/MicronautguideSpec.groovy | 53 ++++++------ .../micronaut/SqsClientBuilderListener.groovy | 52 ++++++++++++ .../SqsClientCreatedEventListener.groovy | 39 +++++++++ .../groovy/example/micronaut/SqsConfig.groovy | 18 ++++ .../micronaut/TestSqsClientFactory.groovy | 45 ---------- .../java/example/micronaut/DemoConsumer.java | 7 +- .../example/micronaut/SqsClientFactory.java | 37 --------- .../example/micronaut/MicronautguideTest.java | 52 ++++++------ .../micronaut/SqsClientBuilderListener.java | 55 ++++++++++++ .../SqsClientCreatedEventListener.java | 39 +++++++++ .../java/example/micronaut/SqsConfig.java | 70 ++++++++++++++++ .../micronaut/TestSqsClientFactory.java | 45 ---------- .../kotlin/example/micronaut/DemoConsumer.kt | 11 ++- .../example/micronaut/SqsClientFactory.kt | 37 --------- .../example/micronaut/LocalStackUtil.kt | 77 ----------------- .../example/micronaut/MicronautguideTest.kt | 53 ++++++------ .../micronaut/SqsClientBuilderListener.kt | 47 +++++++++++ .../SqsClientCreatedEventListener.kt | 42 ++++++++++ .../kotlin/example/micronaut/SqsConfig.kt | 34 ++++++++ .../example/micronaut/TestSqsClientFactory.kt | 48 ----------- guides/micronaut-jms-aws-sqs/metadata.json | 9 +- .../micronaut-jms-aws-sqs.adoc | 79 +++++++++++------- .../src/main/resources/application.properties | 1 + .../src/main/resources/application.yml | 13 --- .../java/example/micronaut/LocalStack.java | 83 ------------------- .../callout-bean-created-event-listener.adoc | 3 +- .../common-localstack-dependencies.adoc | 13 +++ .../common/snippets/common-localstack.adoc | 4 + 30 files changed, 565 insertions(+), 553 deletions(-) delete mode 100644 guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/SqsClientFactory.groovy create mode 100644 guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientBuilderListener.groovy create mode 100644 guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientCreatedEventListener.groovy create mode 100644 guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsConfig.groovy delete mode 100644 guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/TestSqsClientFactory.groovy delete mode 100644 guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/SqsClientFactory.java create mode 100644 guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientBuilderListener.java create mode 100644 guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientCreatedEventListener.java create mode 100644 guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsConfig.java delete mode 100644 guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/TestSqsClientFactory.java delete mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/SqsClientFactory.kt delete mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/LocalStackUtil.kt create mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientBuilderListener.kt create mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientCreatedEventListener.kt create mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsConfig.kt delete mode 100644 guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/TestSqsClientFactory.kt create mode 100644 guides/micronaut-jms-aws-sqs/src/main/resources/application.properties delete mode 100644 guides/micronaut-jms-aws-sqs/src/main/resources/application.yml delete mode 100644 guides/micronaut-jms-aws-sqs/src/test/java/example/micronaut/LocalStack.java create mode 100644 src/docs/common/snippets/common-localstack-dependencies.adoc create mode 100644 src/docs/common/snippets/common-localstack.adoc diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/DemoConsumer.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/DemoConsumer.groovy index 3fb3170de7..a9106bdf3d 100644 --- a/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/DemoConsumer.groovy +++ b/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/DemoConsumer.groovy @@ -16,25 +16,25 @@ package example.micronaut import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j import io.micronaut.jms.annotations.JMSListener import io.micronaut.jms.annotations.Queue import io.micronaut.messaging.annotation.MessageBody +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.util.concurrent.atomic.AtomicInteger import static io.micronaut.jms.sqs.configuration.SqsConfiguration.CONNECTION_FACTORY_BEAN_NAME @CompileStatic -@Slf4j('LOGGER') @JMSListener(CONNECTION_FACTORY_BEAN_NAME) // <1> class DemoConsumer { - + private static final Logger LOG = LoggerFactory.getLogger(DemoConsumer.class); private final AtomicInteger messageCount = new AtomicInteger(0); - @Queue(value = "demo_queue", concurrency = "1-3") // <2> + @Queue(value = "demo_queue") // <2> void receive(@MessageBody String body) { // <3> - LOGGER.info("Message consumed: {}", body); + LOG.info("Message consumed: {}", body); messageCount.incrementAndGet() } diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/SqsClientFactory.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/SqsClientFactory.groovy deleted file mode 100644 index 14fdbfa5d5..0000000000 --- a/guides/micronaut-jms-aws-sqs/groovy/src/main/groovy/example/micronaut/SqsClientFactory.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut - -import com.amazonaws.regions.Regions -import com.amazonaws.services.sqs.AmazonSQS -import com.amazonaws.services.sqs.AmazonSQSClientBuilder -import groovy.transform.CompileStatic -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider -import io.micronaut.context.annotation.Factory -import io.micronaut.context.annotation.Requires -import io.micronaut.context.env.Environment -import jakarta.inject.Singleton - - -@CompileStatic -@Factory // <1> -@Requires(notEnv = Environment.TEST) -class SqsClientFactory { - - @Singleton - AmazonSQS sqsClient(Environment environment) { // <2> - AmazonSQSClientBuilder - .standard() - .withRegion(Regions.US_EAST_1) // <3> - .withCredentials(new EnvironmentAWSCredentialsProvider(environment)) // <4> - .build() - } -} diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/MicronautguideSpec.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/MicronautguideSpec.groovy index ce4f6309c6..f784bea579 100644 --- a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/MicronautguideSpec.groovy +++ b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/MicronautguideSpec.groovy @@ -15,48 +15,53 @@ */ package example.micronaut +import io.micronaut.core.annotation.NonNull import io.micronaut.http.HttpRequest -import io.micronaut.http.HttpResponse import io.micronaut.http.client.HttpClient import io.micronaut.http.client.annotation.Client import io.micronaut.test.extensions.spock.annotation.MicronautTest import io.micronaut.test.support.TestPropertyProvider import jakarta.inject.Inject +import org.testcontainers.containers.localstack.LocalStackContainer +import org.testcontainers.utility.DockerImageName +import spock.lang.AutoCleanup +import spock.lang.Shared import spock.lang.Specification import spock.util.concurrent.PollingConditions -@MicronautTest -class MicronautguideSpec extends Specification implements TestPropertyProvider { +@MicronautTest // <1> +class MicronautguideSpec extends Specification implements TestPropertyProvider { // <3> + private static DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:latest") + @AutoCleanup + @Shared + private static LocalStackContainer localstack = new LocalStackContainer(localstackImage) + .withServices(LocalStackContainer.Service.SQS) + + @Override + @NonNull + Map getProperties() { + if (!localstack.isRunning()) { + localstack.start() + } + Map.of("aws.access-key-id", localstack.accessKey, + "aws.secret-key", localstack.secretKey, + "aws.region", localstack.region, + "aws.services.sqs.endpoint-override", localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString()) + } @Inject @Client("/") - HttpClient client + HttpClient httpClient @Inject DemoConsumer demoConsumer - void 'test it works'() { - when: - int messageCount = demoConsumer.getMessageCount() - - then: - messageCount == 0 - + void "verify jms consumes message"() { when: - HttpResponse response = client.toBlocking().exchange(HttpRequest.POST('/demo', [:])) - + httpClient.toBlocking().exchange(HttpRequest.POST("/demo", Collections.emptyMap())) then: - new PollingConditions(initialDelay: 3, timeout: 10).eventually { - demoConsumer.getMessageCount() == 1 + new PollingConditions().eventually { + assert 1 == demoConsumer.messageCount } } - - def cleanupSpec() { - LocalStack.close() - } - - @Override - Map getProperties() { - LocalStack.getProperties() - } } diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientBuilderListener.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientBuilderListener.groovy new file mode 100644 index 0000000000..7b5620ecaf --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientBuilderListener.groovy @@ -0,0 +1,52 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut + +import io.micronaut.context.event.BeanCreatedEvent +import io.micronaut.context.event.BeanCreatedEventListener +import io.micronaut.core.annotation.NonNull +import jakarta.inject.Singleton +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.sqs.SqsClientBuilder + +@Singleton // <1> +class SqsClientBuilderListener implements BeanCreatedEventListener { // <2> + + private final SqsConfig sqsConfig + + SqsClientBuilderListener(SqsConfig sqsConfig) { // <3> + this.sqsConfig = sqsConfig + } + + @Override + SqsClientBuilder onCreated(@NonNull BeanCreatedEvent event) { + SqsClientBuilder builder = event.bean + try { + return builder + .endpointOverride(new URI(sqsConfig.sqs.endpointOverride)) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(sqsConfig.accessKeyId, sqsConfig.secretKey) + ) + ) + .region(Region.of(sqsConfig.region)) + } catch (URISyntaxException e) { + throw new RuntimeException(e) + } + } +} diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientCreatedEventListener.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientCreatedEventListener.groovy new file mode 100644 index 0000000000..0c1398c592 --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsClientCreatedEventListener.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut + +import io.micronaut.context.event.BeanCreatedEvent +import io.micronaut.context.event.BeanCreatedEventListener +import jakarta.inject.Singleton +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest + +@Singleton +public class SqsClientCreatedEventListener implements BeanCreatedEventListener { + private static final String QUEUE_NAME = "demo_queue" + @Override + SqsClient onCreated(BeanCreatedEvent event) { + SqsClient client = event.getBean() + if (client.listQueues().queueUrls().stream().noneMatch(it -> it.contains(QUEUE_NAME))) { + client.createQueue( + CreateQueueRequest.builder() + .queueName(QUEUE_NAME) + .build() + ) + } + client + } +} diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsConfig.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsConfig.groovy new file mode 100644 index 0000000000..109a86b2c9 --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/SqsConfig.groovy @@ -0,0 +1,18 @@ +package example.micronaut + +import io.micronaut.context.annotation.ConfigurationBuilder +import io.micronaut.context.annotation.ConfigurationProperties + +@ConfigurationProperties("aws") // <1> +class SqsConfig { + String accessKeyId + String secretKey + String region + + @ConfigurationBuilder(configurationPrefix = "services.sqs") + final Sqs sqs = new Sqs() + + static class Sqs { + String endpointOverride + } +} diff --git a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/TestSqsClientFactory.groovy b/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/TestSqsClientFactory.groovy deleted file mode 100644 index 28f4f1f631..0000000000 --- a/guides/micronaut-jms-aws-sqs/groovy/src/test/groovy/example/micronaut/TestSqsClientFactory.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut - -import com.amazonaws.client.builder.AwsClientBuilder -import com.amazonaws.regions.Regions -import com.amazonaws.services.sqs.AmazonSQS -import com.amazonaws.services.sqs.AmazonSQSClientBuilder -import groovy.transform.CompileStatic -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider -import io.micronaut.context.annotation.Factory -import io.micronaut.context.annotation.Replaces; -import io.micronaut.context.env.Environment -import jakarta.inject.Singleton - -@CompileStatic -@Factory -class TestSqsClientFactory { - - @Singleton - @Replaces(AmazonSQS.class) - AmazonSQS sqsClient(Environment environment) { // <2> - Optional endpointOverride = environment.getProperty("aws.sqs.endpoint-override", String.class); - - AmazonSQSClientBuilder - .standard() - .withCredentials(new EnvironmentAWSCredentialsProvider(environment)) - .withEndpointConfiguration( - new AwsClientBuilder.EndpointConfiguration(endpointOverride.orElseThrow(() -> new IllegalStateException()), Regions.US_EAST_1.getName())) - .build(); - } -} diff --git a/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/DemoConsumer.java b/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/DemoConsumer.java index 5507d502d7..449b1b6344 100644 --- a/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/DemoConsumer.java +++ b/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/DemoConsumer.java @@ -27,13 +27,14 @@ @JMSListener(CONNECTION_FACTORY_BEAN_NAME) // <1> public class DemoConsumer { - private static final Logger LOGGER = LoggerFactory.getLogger(DemoConsumer.class); + + private static final Logger LOG = LoggerFactory.getLogger(DemoConsumer.class); private final AtomicInteger messageCount = new AtomicInteger(0); - @Queue(value = "demo_queue", concurrency = "1-3") // <2> + @Queue(value = "demo_queue") // <2> public void receive(@MessageBody String body) { // <3> - LOGGER.info("Message has been consumed. Message body: {}", body); + LOG.info("Message has been consumed. Message body: {}", body); messageCount.incrementAndGet(); } diff --git a/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/SqsClientFactory.java b/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/SqsClientFactory.java deleted file mode 100644 index 35887bccb6..0000000000 --- a/guides/micronaut-jms-aws-sqs/java/src/main/java/example/micronaut/SqsClientFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut; - -import com.amazonaws.regions.Regions; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClientBuilder; -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider; -import io.micronaut.context.annotation.Factory; -import io.micronaut.context.env.Environment; -import jakarta.inject.Singleton; - -@Factory // <1> -public class SqsClientFactory { - - @Singleton - AmazonSQS sqsClient(Environment environment) { // <2> - return AmazonSQSClientBuilder - .standard() - .withRegion(Regions.US_EAST_1) // <3> - .withCredentials(new EnvironmentAWSCredentialsProvider(environment)) // <4> - .build(); - } -} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/MicronautguideTest.java b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/MicronautguideTest.java index c32e131a4b..0c8e90c645 100644 --- a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/MicronautguideTest.java +++ b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/MicronautguideTest.java @@ -22,21 +22,36 @@ import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import io.micronaut.test.support.TestPropertyProvider; import jakarta.inject.Inject; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.utility.DockerImageName; import java.util.Collections; import java.util.Map; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Testcontainers(disabledWithoutDocker = true) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@MicronautTest -class MicronautguideTest implements TestPropertyProvider { +@MicronautTest // <1> +@TestInstance(TestInstance.Lifecycle.PER_CLASS) // <2> +class MicronautguideTest implements TestPropertyProvider { // <3> + private static DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:latest"); + private static LocalStackContainer localstack = new LocalStackContainer(localstackImage) + .withServices(LocalStackContainer.Service.SQS); + + @Override + public @NonNull Map getProperties() { + if (!localstack.isRunning()) { + localstack.start(); + } + return Map.of("aws.access-key-id", localstack.getAccessKey(), + "aws.secret-key", localstack.getSecretKey(), + "aws.region", localstack.getRegion(), + "aws.services.sqs.endpoint-override", localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString()); + } @Inject @Client("/") HttpClient httpClient; @@ -46,26 +61,9 @@ class MicronautguideTest implements TestPropertyProvider { @Test void testItWorks() { - int messageCount = demoConsumer.getMessageCount(); - Assertions.assertTrue(messageCount == 0); - + assertEquals(0, demoConsumer.getMessageCount()); httpClient.toBlocking().exchange(HttpRequest.POST("/demo", Collections.emptyMap())); - messageCount = demoConsumer.getMessageCount(); - while (messageCount == 0) { - messageCount = demoConsumer.getMessageCount(); - } - - Assertions.assertTrue(messageCount == 1); - } - - @AfterAll - static void afterAll() { - LocalStack.close(); - } - - @Override - @NonNull - public Map getProperties() { - return LocalStack.getProperties(); + await().until(() -> demoConsumer.getMessageCount(), equalTo(1)); + assertEquals(1, demoConsumer.getMessageCount()); } } diff --git a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientBuilderListener.java b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientBuilderListener.java new file mode 100644 index 0000000000..bcb66bc57e --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientBuilderListener.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut; + +import io.micronaut.context.event.BeanCreatedEvent; +import io.micronaut.context.event.BeanCreatedEventListener; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sqs.SqsClientBuilder; + +import java.net.URI; +import java.net.URISyntaxException; + +@Singleton // <1> +class SqsClientBuilderListener implements BeanCreatedEventListener { // <2> + + private final SqsConfig sqsConfig; + + SqsClientBuilderListener(SqsConfig sqsConfig) { // <3> + this.sqsConfig = sqsConfig; + } + + @Override + public SqsClientBuilder onCreated(@NonNull BeanCreatedEvent event) { + SqsClientBuilder builder = event.getBean(); + try { + return builder + .endpointOverride(new URI(sqsConfig.getSqs().getEndpointOverride())) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(sqsConfig.getAccessKeyId(), sqsConfig.getSecretKey()) + ) + ) + .region(Region.of(sqsConfig.getRegion())); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } +} diff --git a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientCreatedEventListener.java b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientCreatedEventListener.java new file mode 100644 index 0000000000..2aaef5d9cc --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsClientCreatedEventListener.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut; + +import io.micronaut.context.event.BeanCreatedEvent; +import io.micronaut.context.event.BeanCreatedEventListener; +import jakarta.inject.Singleton; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; + +@Singleton // <1> +public class SqsClientCreatedEventListener implements BeanCreatedEventListener { // <2> + private static final String QUEUE_NAME = "demo_queue"; + @Override + public SqsClient onCreated(BeanCreatedEvent event) { + SqsClient client = event.getBean(); + if (client.listQueues().queueUrls().stream().noneMatch(it -> it.contains(QUEUE_NAME))) { + client.createQueue( + CreateQueueRequest.builder() + .queueName(QUEUE_NAME) + .build() + ); + } + return client; + } +} diff --git a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsConfig.java b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsConfig.java new file mode 100644 index 0000000000..afc307c7aa --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/SqsConfig.java @@ -0,0 +1,70 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut; + +import io.micronaut.context.annotation.ConfigurationBuilder; +import io.micronaut.context.annotation.ConfigurationProperties; + +@ConfigurationProperties("aws") // <1> +public class SqsConfig { + + private String accessKeyId; + private String secretKey; + private String region; + @ConfigurationBuilder(configurationPrefix = "services.sqs") + final Sqs sqs = new Sqs(); + + public String getAccessKeyId() { + return accessKeyId; + } + + public void setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public Sqs getSqs() { + return sqs; + } + + public static class Sqs { + + private String endpointOverride; + + public String getEndpointOverride() { + return endpointOverride; + } + + public void setEndpointOverride(String endpointOverride) { + this.endpointOverride = endpointOverride; + } + } +} diff --git a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/TestSqsClientFactory.java b/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/TestSqsClientFactory.java deleted file mode 100644 index ad88060b9b..0000000000 --- a/guides/micronaut-jms-aws-sqs/java/src/test/java/example/micronaut/TestSqsClientFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut; - -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClientBuilder; -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider; -import io.micronaut.context.annotation.Factory; -import io.micronaut.context.annotation.Replaces; -import io.micronaut.context.env.Environment; -import jakarta.inject.Singleton; - -import java.util.Optional; - -@Factory -public class TestSqsClientFactory { - - @Singleton - @Replaces(AmazonSQS.class) - AmazonSQS sqsClient(Environment environment) { - Optional endpointOverride = environment.getProperty("aws.sqs.endpoint-override", String.class); - - return AmazonSQSClientBuilder - .standard() - .withCredentials(new EnvironmentAWSCredentialsProvider(environment)) - .withEndpointConfiguration( - new AwsClientBuilder.EndpointConfiguration(endpointOverride.orElseThrow(() -> new IllegalStateException()), Regions.US_EAST_1.getName())) - .build(); - } -} diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/DemoConsumer.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/DemoConsumer.kt index dacdb5b5bb..c9b6b34595 100644 --- a/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/DemoConsumer.kt +++ b/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/DemoConsumer.kt @@ -24,19 +24,18 @@ import java.util.concurrent.atomic.AtomicInteger @JMSListener(CONNECTION_FACTORY_BEAN_NAME) // <1> class DemoConsumer { + private val messageCount = AtomicInteger(0) - @Queue(value = "demo_queue", concurrency = "1-3") // <2> + @Queue(value = "demo_queue") // <2> fun receive(@MessageBody body: String?) { // <3> - LOGGER.info("Message has been consumed. Message body: {}", body) + LOG.info("Message has been consumed. Message body: {}", body) messageCount.incrementAndGet() } - fun getMessageCount(): Int { - return messageCount.toInt() - } + fun getMessageCount() = messageCount.toInt() companion object { - private val LOGGER = LoggerFactory.getLogger(DemoConsumer::class.java) + private val LOG = LoggerFactory.getLogger(DemoConsumer::class.java) } } \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/SqsClientFactory.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/SqsClientFactory.kt deleted file mode 100644 index 6cb26b279e..0000000000 --- a/guides/micronaut-jms-aws-sqs/kotlin/src/main/kotlin/example/micronaut/SqsClientFactory.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut - -import com.amazonaws.regions.Regions -import com.amazonaws.services.sqs.AmazonSQS -import com.amazonaws.services.sqs.AmazonSQSClientBuilder -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider -import io.micronaut.context.annotation.Factory -import io.micronaut.context.env.Environment -import jakarta.inject.Singleton - -@Factory // <1> -class SqsClientFactory { - - @Singleton - fun sqsClient(environment: Environment): AmazonSQS { // <2> - return AmazonSQSClientBuilder - .standard() - .withRegion(Regions.US_EAST_1) // <3> - .withCredentials(EnvironmentAWSCredentialsProvider(environment)) // <4> - .build() - } -} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/LocalStackUtil.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/LocalStackUtil.kt deleted file mode 100644 index eec8cd6338..0000000000 --- a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/LocalStackUtil.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut - -import org.testcontainers.containers.localstack.LocalStackContainer -import org.testcontainers.utility.DockerImageName -import java.lang.InterruptedException -import java.lang.RuntimeException -import java.io.IOException - -object LocalStackUtil { - private const val TAG = "1.1.0" - private const val IMAGE = "localstack/localstack" - private const val DEMO_QUEUE = "demo_queue" - - private var localstack: LocalStackContainer = LocalStackContainer(DockerImageName.parse("$IMAGE:$TAG")) - .withServices(LocalStackContainer.Service.SQS) - - init { - localstack.start() - setupPrerequisites() - } - - fun getLocalStack(): LocalStackContainer { - return localstack - } - - private val endpoint: String - get() = localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() - - private fun secretAccessKey(): String { - return localstack.secretKey - } - - private val region: String - get() = localstack.region - - private fun accessKeyId(): String { - return localstack.accessKey - } - - val properties: Map - get() = mapOf( - "aws.accessKeyId" to accessKeyId(), - "aws.secretKey" to secretAccessKey(), - "aws.region" to region, - "aws.sqs.endpoint-override" to endpoint - ) - - - private fun setupPrerequisites() { - try { - localstack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", DEMO_QUEUE) - } catch (e: InterruptedException) { - throw RuntimeException("Failed to setup the Localstack prerequisites", e) - } catch (e: IOException) { - throw RuntimeException("Failed to setup the Localstack prerequisites", e) - } - } - - fun close() { - localstack.close() - } -} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/MicronautguideTest.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/MicronautguideTest.kt index 8175c75ce8..a5dcedb47b 100644 --- a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/MicronautguideTest.kt +++ b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/MicronautguideTest.kt @@ -22,46 +22,49 @@ import io.micronaut.http.client.annotation.Client import io.micronaut.test.extensions.junit5.annotation.MicronautTest import io.micronaut.test.support.TestPropertyProvider import jakarta.inject.Inject -import org.junit.jupiter.api.AfterAll +import org.awaitility.Awaitility +import org.hamcrest.Matchers import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance -import org.testcontainers.junit.jupiter.Testcontainers -import java.util.* +import org.testcontainers.containers.localstack.LocalStackContainer +import org.testcontainers.utility.DockerImageName -@Testcontainers(disabledWithoutDocker = true) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@MicronautTest -internal class MicronautguideTest : TestPropertyProvider { +@MicronautTest // <1> +@TestInstance(TestInstance.Lifecycle.PER_CLASS) // <2> +internal class MicronautguideTest : TestPropertyProvider { // <3> @Inject @field:Client("/") - lateinit var client : HttpClient + lateinit var httpClient : HttpClient @Inject lateinit var demoConsumer: DemoConsumer - @Test - fun testItWorks() { - var messageCount = demoConsumer.getMessageCount() - Assertions.assertTrue(messageCount == 0) - - client.toBlocking().exchange(HttpRequest.POST("/demo", emptyMap())) - messageCount = demoConsumer.getMessageCount() - while (messageCount == 0) { - messageCount = demoConsumer.getMessageCount() + override fun getProperties(): @NonNull MutableMap { + if (!localstack.isRunning) { + localstack.start() } - Assertions.assertTrue(messageCount == 1) + return mapOf( + "aws.access-key-id" to localstack.accessKey, + "aws.secret-key" to localstack.secretKey, + "aws.region" to localstack.region, + "aws.services.sqs.endpoint-override" to localstack.getEndpointOverride(LocalStackContainer.Service.SQS) + .toString() + ).toMutableMap() } - @NonNull - override fun getProperties(): Map { - return LocalStackUtil.properties + @Test + fun testItWorks() { + Assertions.assertEquals(0, demoConsumer.getMessageCount()) + httpClient.toBlocking().exchange, Any>(HttpRequest.POST("/demo", emptyMap())) + Awaitility.await().until({ demoConsumer.getMessageCount() }, Matchers.equalTo(1)) + Assertions.assertEquals(1, demoConsumer.getMessageCount()) } - @AfterAll - fun afterAll() { - LocalStackUtil.close() + companion object { + private val localstackImage: DockerImageName = DockerImageName.parse("localstack/localstack:latest") + private val localstack: LocalStackContainer = LocalStackContainer(localstackImage) + .withServices(LocalStackContainer.Service.SQS) } - } \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientBuilderListener.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientBuilderListener.kt new file mode 100644 index 0000000000..8b552dca6f --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientBuilderListener.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut + +import io.micronaut.context.event.BeanCreatedEvent +import io.micronaut.context.event.BeanCreatedEventListener +import jakarta.inject.Singleton +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.sqs.SqsClientBuilder +import java.net.URI +import java.net.URISyntaxException + +@Singleton // <1> +class SqsClientBuilderListener(private val sqsConfig: SqsConfig) // <3> + : BeanCreatedEventListener { // <2> + + override fun onCreated(event: BeanCreatedEvent): SqsClientBuilder { + val builder = event.bean + try { + return builder + .endpointOverride(URI(sqsConfig.sqs.endpointOverride!!)) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(sqsConfig.accessKeyId, sqsConfig.secretKey) + ) + ) + .region(Region.of(sqsConfig.region)) + } catch (e: URISyntaxException) { + throw RuntimeException(e) + } + } +} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientCreatedEventListener.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientCreatedEventListener.kt new file mode 100644 index 0000000000..24772319ce --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsClientCreatedEventListener.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut + +import io.micronaut.context.event.BeanCreatedEvent +import io.micronaut.context.event.BeanCreatedEventListener +import jakarta.inject.Singleton +import software.amazon.awssdk.services.sqs.SqsClient +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest + +@Singleton // <1> +class SqsClientCreatedEventListener : BeanCreatedEventListener { // <2> + + override fun onCreated(event: BeanCreatedEvent): SqsClient { + val client = event.bean + if (client.listQueues().queueUrls().stream().noneMatch { it: String -> it.contains(QUEUE_NAME) }) { + client.createQueue( + CreateQueueRequest.builder() + .queueName(QUEUE_NAME) + .build() + ) + } + return client + } + + companion object { + private const val QUEUE_NAME = "demo_queue" + } +} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsConfig.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsConfig.kt new file mode 100644 index 0000000000..f4214408b9 --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/SqsConfig.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.micronaut + +import io.micronaut.context.annotation.ConfigurationBuilder +import io.micronaut.context.annotation.ConfigurationProperties + +@ConfigurationProperties("aws") // <1> +class SqsConfig { + + var accessKeyId: String? = null + var secretKey: String? = null + var region: String? = null + + @ConfigurationBuilder(configurationPrefix = "services.sqs") + val sqs: Sqs = Sqs() + + class Sqs { + var endpointOverride: String? = null + } +} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/TestSqsClientFactory.kt b/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/TestSqsClientFactory.kt deleted file mode 100644 index 1ad8743739..0000000000 --- a/guides/micronaut-jms-aws-sqs/kotlin/src/test/kotlin/example/micronaut/TestSqsClientFactory.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut - -import jakarta.inject.Singleton -import com.amazonaws.services.sqs.AmazonSQS -import com.amazonaws.services.sqs.AmazonSQSClientBuilder -import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider -import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration -import com.amazonaws.regions.Regions -import io.micronaut.context.annotation.Factory -import io.micronaut.context.annotation.Replaces -import io.micronaut.context.env.Environment -import java.lang.IllegalStateException - - -@Factory -class TestSqsClientFactory { - - @Singleton - @Replaces(AmazonSQS::class) - fun sqsClient(environment: Environment): AmazonSQS { - val endpointOverride = environment.getProperty("aws.sqs.endpoint-override", String::class.java) - return AmazonSQSClientBuilder - .standard() - .withCredentials(EnvironmentAWSCredentialsProvider(environment)) - .withEndpointConfiguration( - EndpointConfiguration( - endpointOverride.orElseThrow { IllegalStateException() }, - Regions.US_EAST_1.getName() - ) - ) - .build() - } -} \ No newline at end of file diff --git a/guides/micronaut-jms-aws-sqs/metadata.json b/guides/micronaut-jms-aws-sqs/metadata.json index fb329e0e9f..8bc9b24282 100644 --- a/guides/micronaut-jms-aws-sqs/metadata.json +++ b/guides/micronaut-jms-aws-sqs/metadata.json @@ -4,14 +4,15 @@ "authors": ["Slavko Bodvanski"], "tags": ["jms", "messaging", "aws"], "categories": ["Messaging"], - "maximumJavaVersion": 16, - "skipMavenTests": true, + "languages": ["java", "kotlin"], "publicationDate": "2022-11-28", "apps": [ { "name": "default", - "features": ["jms-sqs", "graalvm"], - "invisibleFeatures": ["localstack"] + "features": ["jms-sqs", "localstack"], + "javaFeatures": [ "awaitility"], + "kotlinFeatures": [ "awaitility"], + "excludeTest": ["DefaultTest"] } ] } diff --git a/guides/micronaut-jms-aws-sqs/micronaut-jms-aws-sqs.adoc b/guides/micronaut-jms-aws-sqs/micronaut-jms-aws-sqs.adoc index 8bccae9f97..0e80e5be81 100644 --- a/guides/micronaut-jms-aws-sqs/micronaut-jms-aws-sqs.adoc +++ b/guides/micronaut-jms-aws-sqs/micronaut-jms-aws-sqs.adoc @@ -5,8 +5,6 @@ common:requirements.adoc[] ** An IAM user with enough permissions to create and manage a queue instances in SQS. ** The AWS CLI configured to use the IAM user above. -common:aws.adoc[] - common:completesolution.adoc[] common:create-app-features.adoc[] @@ -18,36 +16,74 @@ ____ Amazon SQS is a reliable, highly-scalable hosted queue for storing messages as they travel between applications or microservices. Amazon SQS moves data between distributed application components and helps you decouple these components. ____ +=== Configuration + +Enable JMS SQS integration: + +resource:application.properties[] + === Creating a JMS Producer + Create a JMS Producer interface. + source:DemoProducer[] <1> The `JMSProducer` annotation defines this interface as a client that sends messages. <2> The `@Queue` annotation indicates which queue the message should be published to. <3> The `send` method accepts a single parameter which is the payload of a message. === Creating a JMS Consumer + Create a JMS Consumer class. + source:DemoConsumer[] <1> The `@JMSListener` defines the bean as a message listener. <2> The `@Queue` annotation indicates which queue to subscribe to. <3> The `receive` method accepts a single parameter which is the payload of a message. -=== Configure a SQS client factory -Additionally, you’ll need to configure an instance of `com.amazonaws.services.sqs.AmazonSQS` as a bean for AWS authentication, for example: -source:SqsClientFactory[] -callout:factory[1] -<2> Configures `com.amazonaws.services.sqs.AmazonSQS` bean as a singleton instance. -<3> Sets the region to be used by the client. This will be used to determine both the service endpoint (eg: https://sns.us-east-1.amazonaws.com) and signing region (eg: us-east-1) for requests. -<4> Sets the `com.amazonaws.auth.AWSCredentialsProvider` used by the client. In this example sets the provider that reads from the `io.micronaut.context.env.Environment`. - === Creating a Controller + Let’s create a Controller with an endpoint that we will call to verify that message has been sent by the JMS Producer (`DemoProducer`) and then finally received and consumed by the JMS Consumer (`DemoConsumer`). + source:DemoController[] callout:controller[number=1,arg0=/] -<2> Injects `DemoProducer` bean. +callout:constructor-di[number=2,arg0=DemoProducer] <3> Maps a `GET` request to `/demo` path, which attempts to publish a message to a SQS queue instance. <4> Calls `send` method on `DemoProducer` instances providing the message payload. +== Testing + +common:localstack.adoc[] + +common:localstack-dependencies.adoc[] + +First, we create a configuration properties object to encapsulate the configuration of the SQS client, which we will provide via LocalStack. + +test:SqsConfig[] +callout:configuration-properties[1] + +Create a `BeanCreatedEventListener` to override the SQS client endpoint with the LocalStack endpoint. + +test:SqsClientBuilderListener[] +callout:singleton[1] +callout:bean-created-event-listener[2] +callout:constructor-di[number=3,arg0=SqsConfig] + +Create a `BeanCreatedEventListener` to create a SQS queue named `demo_queue`. + +test:SqsClientCreatedEventListener[] +callout:singleton[1] +callout:bean-created-event-listener[2] + +Create a test using the https://java.testcontainers.org/modules/localstack/[Testcontainers LocalStack] module to start a LocalStack container and verify that the message has been sent and received. + +test:MicronautguideTest[] + +callout:micronaut-test[1] +callout:test-instance-per-class[2] +callout:test-property-provider[3] + +common:aws.adoc[] + == Creating a queue instance in Amazon Simple Queue Service (Amazon SQS) You will create a queue with the AWS CLI. See the https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sqs/index.html[AWS CLI `sqs` command] for more information. @@ -60,33 +96,16 @@ aws sqs create-queue --queue-name demo_queue ---- Copy and save the response of the command. You will need the `QueueUrl` to delete the queue after you finish with it. -== Update the Application configuration to use AWS - -=== Add the AWS IAM user access key and secret key placeholders to the Application Configuration -[,diff] -.src/main/resources/application.yml ----- -micronaut: - application: - name: demo - jms: - sqs: - enabled: true -+ aws: -+ access-key-id: ${AWS_ACCESS_KEY_ID} -+ secret-key: ${AWS_SECRET_KEY} ----- - == Running the Application With almost everything in place, you can start the application and try it out. First, set environment variables to configure the queue connection. Then you can start the app. -Create environment variables for `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_KEY`, which will be used in the Micronaut app's `application.yml`: +Create environment variables for `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY`, as defined in https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html[AWS SDK Java v2 - Credential settings retrieval order]: [source,bash] ---- export AWS_ACCESS_KEY_ID= -export AWS_SECRET_KEY= +export AWS_SECRET_ACCESS_KEY= ---- [NOTE] diff --git a/guides/micronaut-jms-aws-sqs/src/main/resources/application.properties b/guides/micronaut-jms-aws-sqs/src/main/resources/application.properties new file mode 100644 index 0000000000..210f774e37 --- /dev/null +++ b/guides/micronaut-jms-aws-sqs/src/main/resources/application.properties @@ -0,0 +1 @@ +micronaut.jms.sqs.enabled=true diff --git a/guides/micronaut-jms-aws-sqs/src/main/resources/application.yml b/guides/micronaut-jms-aws-sqs/src/main/resources/application.yml deleted file mode 100644 index 47ba5bf826..0000000000 --- a/guides/micronaut-jms-aws-sqs/src/main/resources/application.yml +++ /dev/null @@ -1,13 +0,0 @@ -micronaut: - application: - name: demo - jms: - sqs: - enabled: true -aws: - access-key-id: ${AWS_ACCESS_KEY_ID} - secret-key: ${AWS_SECRET_KEY} -netty: - default: - allocator: - max-order: 3 diff --git a/guides/micronaut-jms-aws-sqs/src/test/java/example/micronaut/LocalStack.java b/guides/micronaut-jms-aws-sqs/src/test/java/example/micronaut/LocalStack.java deleted file mode 100644 index a502d9819c..0000000000 --- a/guides/micronaut-jms-aws-sqs/src/test/java/example/micronaut/LocalStack.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017-2024 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.micronaut; - -import io.micronaut.core.util.CollectionUtils; -import org.testcontainers.containers.localstack.LocalStackContainer; -import org.testcontainers.utility.DockerImageName; - -import java.io.IOException; -import java.util.Map; - -public class LocalStack { - private static final String TAG = "1.1.0"; - private static final String IMAGE = "localstack/localstack"; - private static LocalStackContainer localstack; - - private static final String DEMO_QUEUE = "demo_queue"; - - static LocalStackContainer getLocalStack() { - init(); - return localstack; - } - - private static String getEndpoint() { - return getLocalStack().getEndpointOverride(LocalStackContainer.Service.SQS).toString(); - } - - private static String secretAccessKey() { - return getLocalStack().getSecretKey(); - } - - private static String getRegion() { - return getLocalStack().getRegion(); - } - - private static String accessKeyId() { - return getLocalStack().getAccessKey(); - } - - public static Map getProperties() { - return CollectionUtils.mapOf( - "aws.accessKeyId", accessKeyId(), - "aws.secretKey", secretAccessKey(), - "aws.region", getRegion(), - "aws.sqs.endpoint-override", getEndpoint()); - } - - public static void init() { - if (localstack == null) { - localstack = new LocalStackContainer(DockerImageName.parse(IMAGE + ":" + TAG)) - .withServices(LocalStackContainer.Service.SQS); - localstack.start(); - setupPrerequisites(); - } - } - - public static void setupPrerequisites() { - try { - localstack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", DEMO_QUEUE); - } catch (InterruptedException | IOException e) { - throw new RuntimeException("Failed to setup the Localstack prerequisites", e); - } - } - - public static void close() { - if (localstack != null) { - localstack.close(); - } - } -} diff --git a/src/docs/common/callouts/callout-bean-created-event-listener.adoc b/src/docs/common/callouts/callout-bean-created-event-listener.adoc index 93a57ce7b5..3f71ceb5a3 100644 --- a/src/docs/common/callouts/callout-bean-created-event-listener.adoc +++ b/src/docs/common/callouts/callout-bean-created-event-listener.adoc @@ -1,2 +1 @@ -Creating a `@Singleton` that implements `BeanCreatedEventListener` allows you to provide extra configuration to -beans after creation. \ No newline at end of file +Creating a `@Singleton` that implements `BeanCreatedEventListener` allows you to provide extra configuration to beans after creation. \ No newline at end of file diff --git a/src/docs/common/snippets/common-localstack-dependencies.adoc b/src/docs/common/snippets/common-localstack-dependencies.adoc new file mode 100644 index 0000000000..5c638c50cb --- /dev/null +++ b/src/docs/common/snippets/common-localstack-dependencies.adoc @@ -0,0 +1,13 @@ +=== LocalStack Dependencies + +Add the following dependencies to your test classpath: + +:dependencies: + +dependency:junit-jupiter[groupId=org.testcontainers,scope=test] +dependency:localstack[groupId=org.testcontainers,scope=test] +dependency:testcontainers[groupId=org.testcontainers,scope=test] + +:dependencies: + + diff --git a/src/docs/common/snippets/common-localstack.adoc b/src/docs/common/snippets/common-localstack.adoc new file mode 100644 index 0000000000..23ef1c4bbb --- /dev/null +++ b/src/docs/common/snippets/common-localstack.adoc @@ -0,0 +1,4 @@ +To test, we will use https://www.localstack.cloud[LocalStack]. +____ +Develop and test your AWS applications locally to reduce development time and increase product velocity. Reduce unnecessary AWS spend and remove the complexity and risk of maintaining AWS dev accounts. +____ \ No newline at end of file