diff --git a/pom.xml b/pom.xml index 96538ab75..b837ab028 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ spring-cloud-aws-starters/spring-cloud-aws-starter-ses spring-cloud-aws-starters/spring-cloud-aws-starter-sns spring-cloud-aws-starters/spring-cloud-aws-starter-sqs + spring-cloud-aws-starters/spring-cloud-aws-starter-kinesis spring-cloud-aws-samples spring-cloud-aws-test spring-cloud-aws-modulith diff --git a/spring-cloud-aws-autoconfigure/pom.xml b/spring-cloud-aws-autoconfigure/pom.xml index a0eee3ee5..c1213fca2 100644 --- a/spring-cloud-aws-autoconfigure/pom.xml +++ b/spring-cloud-aws-autoconfigure/pom.xml @@ -181,6 +181,21 @@ kms true + + software.amazon.kinesis + amazon-kinesis-client + true + + + com.amazonaws + amazon-kinesis-producer + true + + + software.amazon.awssdk + kinesis + true + software.amazon.encryption.s3 amazon-s3-encryption-client-java diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAsyncClientCustomizer.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAsyncClientCustomizer.java new file mode 100644 index 000000000..2c16d1aa0 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAsyncClientCustomizer.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2022 the original author or 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 io.awspring.cloud.autoconfigure.kinesis; + +import io.awspring.cloud.autoconfigure.AwsClientCustomizer; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClientBuilder; + +/** + * Callback interface that can be used to customize a {@link KinesisAsyncClientBuilder}. + * + * @author Matej Nedic + * @since 4.0.0 + */ +@FunctionalInterface +public interface KinesisAsyncClientCustomizer extends AwsClientCustomizer { +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfiguration.java new file mode 100644 index 000000000..2a221a3b3 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2025 the original author or 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 io.awspring.cloud.autoconfigure.kinesis; + +import io.awspring.cloud.autoconfigure.AwsAsyncClientCustomizer; +import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer; +import io.awspring.cloud.autoconfigure.core.AwsConnectionDetails; +import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration; +import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; + +@AutoConfiguration +@ConditionalOnClass({ KinesisAsyncClient.class }) +@EnableConfigurationProperties({ KinesisProperties.class }) +@AutoConfigureAfter({ CredentialsProviderAutoConfiguration.class, RegionProviderAutoConfiguration.class }) +@ConditionalOnProperty(value = "spring.cloud.aws.kinesis.enabled", havingValue = "true", matchIfMissing = true) +public class KinesisAutoConfiguration { + + @ConditionalOnMissingBean + @Bean + public KinesisAsyncClient kinesisAsyncClient(KinesisProperties properties, + AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider connectionDetails, + ObjectProvider kinesisAsyncClientCustomizer, + ObjectProvider awsSyncClientCustomizers) { + return awsClientBuilderConfigurer + .configureAsyncClient(KinesisAsyncClient.builder(), properties, connectionDetails.getIfAvailable(), + kinesisAsyncClientCustomizer.orderedStream(), awsSyncClientCustomizers.orderedStream()) + .build(); + } +} diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisProperties.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisProperties.java new file mode 100644 index 000000000..a809a0e18 --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/kinesis/KinesisProperties.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2025 the original author or 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 io.awspring.cloud.autoconfigure.kinesis; + +import static io.awspring.cloud.autoconfigure.kinesis.KinesisProperties.PREFIX; + +import io.awspring.cloud.autoconfigure.AwsClientProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties related to KinesisClient + * + * @author Matej Nedic + * @since 4.0.0 + */ +@ConfigurationProperties(prefix = PREFIX) +public class KinesisProperties extends AwsClientProperties { + /** + * The prefix used for AWS Kinesis configuration. + */ + public static final String PREFIX = "spring.cloud.aws.kinesis"; +} diff --git a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 657cbf245..36bdffad1 100644 --- a/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-aws-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -15,3 +15,4 @@ io.awspring.cloud.autoconfigure.config.secretsmanager.SecretsManagerAutoConfigur io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreReloadAutoConfiguration io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreAutoConfiguration io.awspring.cloud.autoconfigure.config.s3.S3ReloadAutoConfiguration +io.awspring.cloud.autoconfigure.kinesis.KinesisAutoConfiguration diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfigurationTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfigurationTest.java new file mode 100644 index 000000000..03fdbf00e --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisAutoConfigurationTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2025 the original author or 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 io.awspring.cloud.autoconfigure.kinesis; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.awspring.cloud.autoconfigure.ConfiguredAwsClient; +import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration; +import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration; +import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration; +import java.net.URI; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; + +/** + * Tests for {@link KinesisAutoConfiguration}. + * + * @author Matej Nedic + */ +class KinesisAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues("spring.cloud.aws.region.static:eu-west-1", + "spring.cloud.aws.credentials.access-key:noop", "spring.cloud.aws.credentials.secret-key:noop") + .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, + CredentialsProviderAutoConfiguration.class, KinesisAutoConfiguration.class)); + + @Test + void disableKinesisIntegration() { + this.contextRunner.withPropertyValues("spring.cloud.aws.kinesis.enabled:false").run(context -> { + assertThat(context).doesNotHaveBean(KinesisAsyncClient.class); + }); + } + + @Test + void withCustomEndpoint() { + this.contextRunner.withPropertyValues("spring.cloud.aws.kinesis.endpoint:http://localhost:8090") + .run(context -> { + ConfiguredAwsClient client = new ConfiguredAwsClient(context.getBean(KinesisAsyncClient.class)); + assertThat(client.getEndpoint()).isEqualTo(URI.create("http://localhost:8090")); + assertThat(client.isEndpointOverridden()).isTrue(); + }); + } +} diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisClientCustomizerTests.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisClientCustomizerTests.java new file mode 100644 index 000000000..a4043f14f --- /dev/null +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/kinesis/KinesisClientCustomizerTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2025 the original author or 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 io.awspring.cloud.autoconfigure.kinesis; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.awspring.cloud.autoconfigure.ConfiguredAwsClient; +import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration; +import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration; +import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; + +public class KinesisClientCustomizerTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues("spring.cloud.aws.region.static:eu-west-1", + "spring.cloud.aws.credentials.access-key:noop", "spring.cloud.aws.credentials.secret-key:noop") + .withConfiguration(AutoConfigurations.of(AwsAutoConfiguration.class, RegionProviderAutoConfiguration.class, + CredentialsProviderAutoConfiguration.class, KinesisAutoConfiguration.class)); + + @Test + void customKinesisClientCustomizer() { + contextRunner.withUserConfiguration(KinesisClientCustomizerTests.CustomizerConfig.class).run(context -> { + ConfiguredAwsClient kinesisAsyncClient = new ConfiguredAwsClient(context.getBean(KinesisAsyncClient.class)); + assertThat(kinesisAsyncClient.getApiCallTimeout()).describedAs("sets property from first customizer") + .isEqualTo(Duration.ofMillis(2001)); + assertThat(kinesisAsyncClient.getApiCallAttemptTimeout()) + .describedAs("sets property from second customizer").isEqualTo(Duration.ofMillis(2002)); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CustomizerConfig { + + @Bean + KinesisAsyncClientCustomizer kinesisClientCustomizer() { + return builder -> { + builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> { + c.apiCallTimeout(Duration.ofMillis(2001)); + })); + }; + } + + @Bean + KinesisAsyncClientCustomizer kinesisClientCustomizer2() { + return builder -> { + builder.overrideConfiguration(builder.overrideConfiguration().copy(c -> { + c.apiCallAttemptTimeout(Duration.ofMillis(2002)); + })); + }; + } + } +} diff --git a/spring-cloud-aws-dependencies/pom.xml b/spring-cloud-aws-dependencies/pom.xml index df111f992..1d5b4ca6d 100644 --- a/spring-cloud-aws-dependencies/pom.xml +++ b/spring-cloud-aws-dependencies/pom.xml @@ -33,6 +33,8 @@ 2.0.3 2.0.0-M2 2.3.0 + 2.7.0 + 0.15.12 @@ -68,6 +70,13 @@ true + + software.amazon.kinesis + amazon-kinesis-client + ${amazon.kinesis.client} + true + + software.amazon.encryption.s3 amazon-s3-encryption-client-java @@ -75,6 +84,13 @@ true + + com.amazonaws + amazon-kinesis-producer + ${amazon.kinesis.producer} + true + + software.amazon.s3.accessgrants aws-s3-accessgrants-java-plugin diff --git a/spring-cloud-aws-starters/spring-cloud-aws-starter-kinesis/pom.xml b/spring-cloud-aws-starters/spring-cloud-aws-starter-kinesis/pom.xml new file mode 100644 index 000000000..ac9bd3a8e --- /dev/null +++ b/spring-cloud-aws-starters/spring-cloud-aws-starter-kinesis/pom.xml @@ -0,0 +1,37 @@ + + + + + spring-cloud-aws + io.awspring.cloud + 4.0.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + + spring-cloud-aws-starter-kinesis + Spring Cloud AWS Kinesis Starter + Spring Cloud AWS Kinesis Starter + + + + io.awspring.cloud + spring-cloud-aws-starter + + + software.amazon.awssdk + kinesis + + + software.amazon.kinesis + amazon-kinesis-client + + + com.amazonaws + amazon-kinesis-producer + + +