diff --git a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfiguration.java b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfiguration.java index 6a37d350e..4976e7aab 100644 --- a/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfiguration.java +++ b/spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfiguration.java @@ -15,13 +15,19 @@ */ package io.awspring.cloud.autoconfigure.dynamodb; +import java.io.IOException; +import java.util.Optional; + import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer; import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer; import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration; import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration; -import io.awspring.cloud.dynamodb.*; -import java.io.IOException; -import java.util.Optional; +import io.awspring.cloud.dynamodb.DefaultDynamoDbTableNameResolver; +import io.awspring.cloud.dynamodb.DefaultDynamoDbTableSchemaResolver; +import io.awspring.cloud.dynamodb.DynamoDbOperations; +import io.awspring.cloud.dynamodb.DynamoDbTableNameResolver; +import io.awspring.cloud.dynamodb.DynamoDbTableSchemaResolver; +import io.awspring.cloud.dynamodb.DynamoDbTemplate; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -36,10 +42,14 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; import software.amazon.awssdk.regions.providers.AwsRegionProvider; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClientBuilder; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; +import software.amazon.dax.ClusterDaxAsyncClient; import software.amazon.dax.ClusterDaxClient; /** @@ -51,7 +61,7 @@ */ @AutoConfiguration @EnableConfigurationProperties(DynamoDbProperties.class) -@ConditionalOnClass({ DynamoDbClient.class, DynamoDbEnhancedClient.class, DynamoDbTemplate.class }) +@ConditionalOnClass({ DynamoDbClient.class, DynamoDbAsyncClient.class }) @AutoConfigureAfter({ CredentialsProviderAutoConfiguration.class, RegionProviderAutoConfiguration.class }) @ConditionalOnProperty(name = "spring.cloud.aws.dynamodb.enabled", havingValue = "true", matchIfMissing = true) public class DynamoDbAutoConfiguration { @@ -60,39 +70,59 @@ public class DynamoDbAutoConfiguration { @ConditionalOnClass(name = "software.amazon.dax.ClusterDaxClient") static class DaxDynamoDbClient { - @ConditionalOnMissingBean - @Bean - public DynamoDbClient dynamoDbClient(DynamoDbProperties properties, AwsCredentialsProvider credentialsProvider, - AwsRegionProvider regionProvider) throws IOException { - DaxProperties daxProperties = properties.getDax(); - + private software.amazon.dax.Configuration.Builder toAwsDaxConfiguration(DaxProperties daxProperties) { PropertyMapper propertyMapper = PropertyMapper.get(); software.amazon.dax.Configuration.Builder configuration = software.amazon.dax.Configuration.builder(); propertyMapper.from(daxProperties.getIdleTimeoutMillis()).whenNonNull() - .to(configuration::idleTimeoutMillis); + .to(configuration::idleTimeoutMillis); propertyMapper.from(daxProperties.getConnectionTtlMillis()).whenNonNull() - .to(configuration::connectionTtlMillis); + .to(configuration::connectionTtlMillis); propertyMapper.from(daxProperties.getConnectTimeoutMillis()).whenNonNull() - .to(configuration::connectTimeoutMillis); + .to(configuration::connectTimeoutMillis); propertyMapper.from(daxProperties.getRequestTimeoutMillis()).whenNonNull() - .to(configuration::requestTimeoutMillis); + .to(configuration::requestTimeoutMillis); propertyMapper.from(daxProperties.getWriteRetries()).whenNonNull().to(configuration::writeRetries); propertyMapper.from(daxProperties.getReadRetries()).whenNonNull().to(configuration::readRetries); propertyMapper.from(daxProperties.getClusterUpdateIntervalMillis()).whenNonNull() - .to(configuration::clusterUpdateIntervalMillis); + .to(configuration::clusterUpdateIntervalMillis); propertyMapper.from(daxProperties.getEndpointRefreshTimeoutMillis()).whenNonNull() - .to(configuration::endpointRefreshTimeoutMillis); + .to(configuration::endpointRefreshTimeoutMillis); propertyMapper.from(daxProperties.getMaxConcurrency()).whenNonNull().to(configuration::maxConcurrency); propertyMapper.from(daxProperties.getMaxPendingConnectionAcquires()).whenNonNull() - .to(configuration::maxPendingConnectionAcquires); + .to(configuration::maxPendingConnectionAcquires); propertyMapper.from(daxProperties.getSkipHostNameVerification()).whenNonNull() - .to(configuration::skipHostNameVerification); + .to(configuration::skipHostNameVerification); + + return configuration; + } + + @ConditionalOnMissingBean + @Bean + public DynamoDbClient daxDynamoDbClient(DynamoDbProperties properties, + AwsCredentialsProvider credentialsProvider, + AwsRegionProvider regionProvider) throws IOException { + DaxProperties daxProperties = properties.getDax(); + + software.amazon.dax.Configuration.Builder configuration = toAwsDaxConfiguration(daxProperties); configuration.region(AwsClientBuilderConfigurer.resolveRegion(properties, regionProvider)) .credentialsProvider(credentialsProvider).url(properties.getDax().getUrl()); return ClusterDaxClient.builder().overrideConfiguration(configuration.build()).build(); } + @ConditionalOnMissingBean + @Bean + public DynamoDbAsyncClient daxDynamoDbAsyncClient(DynamoDbProperties properties, + AwsCredentialsProvider credentialsProvider, + AwsRegionProvider regionProvider) throws IOException { + DaxProperties daxProperties = properties.getDax(); + + software.amazon.dax.Configuration.Builder configuration = toAwsDaxConfiguration(daxProperties); + + configuration.region(AwsClientBuilderConfigurer.resolveRegion(properties, regionProvider)) + .credentialsProvider(credentialsProvider).url(properties.getDax().getUrl()); + return ClusterDaxAsyncClient.builder().overrideConfiguration(configuration.build()).build(); + } } @Conditional(MissingDaxUrlCondition.class) @@ -101,31 +131,61 @@ static class StandardDynamoDbClient { @ConditionalOnMissingBean @Bean - public DynamoDbClient dynamoDbClient(AwsClientBuilderConfigurer awsClientBuilderConfigurer, + public DynamoDbClient standardDynamoDbClient(AwsClientBuilderConfigurer awsClientBuilderConfigurer, ObjectProvider> configurer, DynamoDbProperties properties) { return awsClientBuilderConfigurer .configure(DynamoDbClient.builder(), properties, configurer.getIfAvailable()).build(); } + } + + @Conditional(MissingDaxUrlCondition.class) + @Configuration(proxyBeanMethods = false) + static class StandardDynamoDbAsyncClient { + + @ConditionalOnMissingBean + @Bean + public DynamoDbAsyncClient standardDynamoDbAsyncClient(AwsClientBuilderConfigurer awsClientBuilderConfigurer, + ObjectProvider> configurer, + DynamoDbProperties properties) { + return awsClientBuilderConfigurer + .configure(DynamoDbAsyncClient.builder(), properties, configurer.getIfAvailable()).build(); + } } - @ConditionalOnMissingBean - @Bean - public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) { - return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); + @ConditionalOnClass(name = "software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient") + @Configuration(proxyBeanMethods = false) + static class DynamoDbEnhancedClientConfiguration { + @ConditionalOnMissingBean + @ConditionalOnClass(DynamoDbEnhancedClient.class) + @Bean + public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) { + return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); + } + + @ConditionalOnMissingBean + @ConditionalOnClass(DynamoDbEnhancedAsyncClient.class) + @Bean + public DynamoDbEnhancedAsyncClient dynamoDbEnhancedAsyncClient(DynamoDbAsyncClient dynamoDbClient) { + return DynamoDbEnhancedAsyncClient.builder().dynamoDbClient(dynamoDbClient).build(); + } } - @ConditionalOnMissingBean(DynamoDbOperations.class) - @Bean - public DynamoDbTemplate dynamoDBTemplate(DynamoDbProperties properties, - DynamoDbEnhancedClient dynamoDbEnhancedClient, Optional tableSchemaResolver, - Optional tableNameResolver) { - DynamoDbTableSchemaResolver tableSchemaRes = tableSchemaResolver + @ConditionalOnClass(name = "io.awspring.cloud.dynamodb.DynamoDbOperations") + @Configuration(proxyBeanMethods = false) + static class DynamoDbTemplateConfiguration { + @ConditionalOnMissingBean(DynamoDbOperations.class) + @Bean + public DynamoDbTemplate dynamoDBTemplate(DynamoDbProperties properties, + DynamoDbEnhancedClient dynamoDbEnhancedClient, Optional tableSchemaResolver, + Optional tableNameResolver) { + DynamoDbTableSchemaResolver tableSchemaRes = tableSchemaResolver .orElseGet(DefaultDynamoDbTableSchemaResolver::new); - DynamoDbTableNameResolver tableNameRes = tableNameResolver + DynamoDbTableNameResolver tableNameRes = tableNameResolver .orElseGet(() -> new DefaultDynamoDbTableNameResolver(properties.getTablePrefix())); - return new DynamoDbTemplate(dynamoDbEnhancedClient, tableSchemaRes, tableNameRes); + return new DynamoDbTemplate(dynamoDbEnhancedClient, tableSchemaRes, tableNameRes); + } } static class MissingDaxUrlCondition extends NoneNestedConditions { diff --git a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfigurationTest.java b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfigurationTest.java index 178327a74..4f6df76e9 100644 --- a/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfigurationTest.java +++ b/spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfigurationTest.java @@ -40,8 +40,10 @@ import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; +import software.amazon.dax.ClusterDaxAsyncClient; import software.amazon.dax.ClusterDaxClient; /** @@ -96,6 +98,7 @@ void withDynamoDbClientCustomEndpoint() { contextRunner.withPropertyValues("spring.cloud.aws.dynamodb.endpoint:http://localhost:8090") .run(context -> { assertThat(context).hasSingleBean(DynamoDbClient.class); + assertThat(context).hasSingleBean(DynamoDbAsyncClient.class); assertThat(context).hasSingleBean(DynamoDbTemplate.class); assertThat(context).hasSingleBean(DynamoDbEnhancedClient.class); @@ -109,6 +112,7 @@ void withDynamoDbClientCustomEndpoint() { void dynamoDbClientConfiguredSinceNoUrl() { contextRunner.run(context -> { assertThat(context).hasSingleBean(DynamoDbClient.class); + assertThat(context).hasSingleBean(DynamoDbAsyncClient.class); assertThat(context).hasSingleBean(DynamoDbTemplate.class); assertThat(context).hasSingleBean(DynamoDbEnhancedClient.class); assertThat(context).doesNotHaveBean(ClusterDaxClient.class); @@ -170,6 +174,7 @@ void defaultsAreUsedWhenPropertiesAreNotSet() { .run(context -> { ConfiguredDaxClient daxClient = new ConfiguredDaxClient( context.getBean(ClusterDaxClient.class)); + assertThat(context).hasSingleBean(ClusterDaxAsyncClient.class); assertThat(daxClient.getUrl()) .isEqualTo("dax://something.dax-clusters.us-east-1.amazonaws.com"); assertThat(daxClient.getWriteRetries()).isEqualTo(2); @@ -211,6 +216,7 @@ void clusterDaxClient_CustomUrl_DefaultValues() { .run(context -> { ConfiguredDaxClient daxClient = new ConfiguredDaxClient( context.getBean(ClusterDaxClient.class)); + assertThat(context).hasSingleBean(ClusterDaxAsyncClient.class); assertThat(daxClient.getUrl()) .isEqualTo("dax://something.dax-clusters.us-east-1.amazonaws.com"); assertThat(daxClient.getWriteRetries()).isEqualTo(2); @@ -237,6 +243,7 @@ void customTableResolverResolverCanBeConfigured() { assertThat(dynamoDBDynamoDbTableNameResolver) .isInstanceOf(CustomDynamoDBDynamoDbTableNameResolver.class); + assertThat(context).hasSingleBean(ClusterDaxAsyncClient.class); }); }