diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java index 47f6cd5354..3c539a5de6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -50,6 +50,12 @@ import org.springframework.context.annotation.Import; @ConditionalOnEnabledTracing public class ZipkinAutoConfiguration { + @Bean + @ConditionalOnMissingBean(ZipkinConnectionDetails.class) + PropertiesZipkinConnectionDetails zipkinConnectionDetails(ZipkinProperties properties) { + return new PropertiesZipkinConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean public BytesEncoder spanBytesEncoder() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java index f1c33fc406..e2ad6a0b55 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationTests.java @@ -64,6 +64,25 @@ class ZipkinAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(BytesEncoder.class)); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesZipkinConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { + this.contextRunner.withBean(ZipkinConnectionDetails.class, () -> new ZipkinConnectionDetails() { + + @Override + public String getSpanEndpoint() { + return "http://localhost"; + } + + }) + .run((context) -> assertThat(context).hasSingleBean(ZipkinConnectionDetails.class) + .doesNotHaveBean(PropertiesZipkinConnectionDetails.class)); + } + @Configuration(proxyBeanMethods = false) private static class CustomConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/PropertiesRabbitConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/PropertiesRabbitConnectionDetails.java index 1b3359afc0..3e4c485d44 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/PropertiesRabbitConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/PropertiesRabbitConnectionDetails.java @@ -25,13 +25,12 @@ import java.util.List; * @author Moritz Halbritter * @author Andy Wilkinson * @author Phillip Webb - * @since 3.1.0 */ -public class PropertiesRabbitConnectionDetails implements RabbitConnectionDetails { +class PropertiesRabbitConnectionDetails implements RabbitConnectionDetails { private final RabbitProperties properties; - public PropertiesRabbitConnectionDetails(RabbitProperties properties) { + PropertiesRabbitConnectionDetails(RabbitProperties properties) { this.properties = properties; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java index 78477031a2..bd9fb0daa0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java @@ -82,22 +82,24 @@ public class RabbitAutoConfiguration { private final RabbitProperties properties; - private final RabbitConnectionDetails connectionDetails; - protected RabbitConnectionFactoryCreator(RabbitProperties properties, ObjectProvider connectionDetails) { this.properties = properties; - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesRabbitConnectionDetails(properties)); + } + + @Bean + @ConditionalOnMissingBean(RabbitConnectionDetails.class) + RabbitConnectionDetails rabbitConnectionDetails() { + return new PropertiesRabbitConnectionDetails(this.properties); } @Bean @ConditionalOnMissingBean RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, - ObjectProvider credentialsProvider, + RabbitConnectionDetails connectionDetails, ObjectProvider credentialsProvider, ObjectProvider credentialsRefreshService) { RabbitConnectionFactoryBeanConfigurer configurer = new RabbitConnectionFactoryBeanConfigurer(resourceLoader, - this.properties, this.connectionDetails); + this.properties, connectionDetails); configurer.setCredentialsProvider(credentialsProvider.getIfUnique()); configurer.setCredentialsRefreshService(credentialsRefreshService.getIfUnique()); return configurer; @@ -105,10 +107,10 @@ public class RabbitAutoConfiguration { @Bean @ConditionalOnMissingBean - CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer( + CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(RabbitConnectionDetails connectionDetails, ObjectProvider connectionNameStrategy) { CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(this.properties, - this.connectionDetails); + connectionDetails); configurer.setConnectionNameStrategy(connectionNameStrategy.getIfUnique()); return configurer; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java index 5ea0543d70..5d7e039050 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java @@ -84,13 +84,14 @@ public class CassandraAutoConfiguration { private final CassandraProperties properties; - private final CassandraConnectionDetails connectionDetails; - - CassandraAutoConfiguration(CassandraProperties properties, - ObjectProvider connectionDetails) { + CassandraAutoConfiguration(CassandraProperties properties) { this.properties = properties; - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesCassandraConnectionDetails(properties)); + } + + @Bean + @ConditionalOnMissingBean(CassandraConnectionDetails.class) + PropertiesCassandraConnectionDetails cassandraConnectionDetails() { + return new PropertiesCassandraConnectionDetails(this.properties); } @Bean @@ -104,24 +105,25 @@ public class CassandraAutoConfiguration { @ConditionalOnMissingBean @Scope("prototype") public CqlSessionBuilder cassandraSessionBuilder(DriverConfigLoader driverConfigLoader, + CassandraConnectionDetails connectionDetails, ObjectProvider builderCustomizers) { CqlSessionBuilder builder = CqlSession.builder().withConfigLoader(driverConfigLoader); - configureAuthentication(builder); - configureSsl(builder); + configureAuthentication(builder, connectionDetails); + configureSsl(builder, connectionDetails); builder.withKeyspace(this.properties.getKeyspaceName()); builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } - private void configureAuthentication(CqlSessionBuilder builder) { - String username = this.connectionDetails.getUsername(); + private void configureAuthentication(CqlSessionBuilder builder, CassandraConnectionDetails connectionDetails) { + String username = connectionDetails.getUsername(); if (username != null) { - builder.withAuthCredentials(username, this.connectionDetails.getPassword()); + builder.withAuthCredentials(username, connectionDetails.getPassword()); } } - private void configureSsl(CqlSessionBuilder builder) { - if (this.connectionDetails instanceof PropertiesCassandraConnectionDetails && this.properties.isSsl()) { + private void configureSsl(CqlSessionBuilder builder, CassandraConnectionDetails connectionDetails) { + if (connectionDetails instanceof PropertiesCassandraConnectionDetails && this.properties.isSsl()) { try { builder.withSslContext(SSLContext.getDefault()); } @@ -133,18 +135,18 @@ public class CassandraAutoConfiguration { @Bean(destroyMethod = "") @ConditionalOnMissingBean - public DriverConfigLoader cassandraDriverConfigLoader( + public DriverConfigLoader cassandraDriverConfigLoader(CassandraConnectionDetails connectionDetails, ObjectProvider builderCustomizers) { ProgrammaticDriverConfigLoaderBuilder builder = new DefaultProgrammaticDriverConfigLoaderBuilder( - () -> cassandraConfiguration(), DefaultDriverConfigLoader.DEFAULT_ROOT_PATH); + () -> cassandraConfiguration(connectionDetails), DefaultDriverConfigLoader.DEFAULT_ROOT_PATH); builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } - private Config cassandraConfiguration() { + private Config cassandraConfiguration(CassandraConnectionDetails connectionDetails) { ConfigFactory.invalidateCaches(); Config config = ConfigFactory.defaultOverrides(); - config = config.withFallback(mapConfig()); + config = config.withFallback(mapConfig(connectionDetails)); if (this.properties.getConfig() != null) { config = config.withFallback(loadConfig(this.properties.getConfig())); } @@ -162,24 +164,24 @@ public class CassandraAutoConfiguration { } } - private Config mapConfig() { + private Config mapConfig(CassandraConnectionDetails connectionDetails) { CassandraDriverOptions options = new CassandraDriverOptions(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(this.properties.getSessionName()) .whenHasText() .to((sessionName) -> options.add(DefaultDriverOption.SESSION_NAME, sessionName)); - map.from(this.connectionDetails.getUsername()) + map.from(connectionDetails.getUsername()) .to((value) -> options.add(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, value) - .add(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, this.connectionDetails.getPassword())); + .add(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, connectionDetails.getPassword())); map.from(this.properties::getCompression) .to((compression) -> options.add(DefaultDriverOption.PROTOCOL_COMPRESSION, compression)); mapConnectionOptions(options); mapPoolingOptions(options); mapRequestOptions(options); mapControlConnectionOptions(options); - map.from(mapContactPoints()) + map.from(mapContactPoints(connectionDetails)) .to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints)); - map.from(this.connectionDetails.getLocalDatacenter()) + map.from(connectionDetails.getLocalDatacenter()) .whenHasText() .to((localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter)); return options.build(); @@ -244,11 +246,8 @@ public class CassandraAutoConfiguration { .to((timeout) -> options.add(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT, timeout)); } - private List mapContactPoints() { - return this.connectionDetails.getContactPoints() - .stream() - .map((node) -> node.host() + ":" + node.port()) - .toList(); + private List mapContactPoints(CassandraConnectionDetails connectionDetails) { + return connectionDetails.getContactPoints().stream().map((node) -> node.host() + ":" + node.port()).toList(); } private static class CassandraDriverOptions { @@ -289,7 +288,7 @@ public class CassandraAutoConfiguration { /** * Adapts {@link CassandraProperties} to {@link CassandraConnectionDetails}. */ - private static final class PropertiesCassandraConnectionDetails implements CassandraConnectionDetails { + static final class PropertiesCassandraConnectionDetails implements CassandraConnectionDetails { private final CassandraProperties properties; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java index 2a4d5fe7f9..a609e07460 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java @@ -69,34 +69,37 @@ public class CouchbaseAutoConfiguration { private final CouchbaseProperties properties; - private final CouchbaseConnectionDetails connectionDetails; - CouchbaseAutoConfiguration(CouchbaseProperties properties, ObjectProvider connectionDetails) { this.properties = properties; - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesCouchbaseConnectionDetails(properties)); + } + + @Bean + @ConditionalOnMissingBean(CouchbaseConnectionDetails.class) + PropertiesCouchbaseConnectionDetails couchbaseConnectionDetails() { + return new PropertiesCouchbaseConnectionDetails(this.properties); } @Bean @ConditionalOnMissingBean - public ClusterEnvironment couchbaseClusterEnvironment( + public ClusterEnvironment couchbaseClusterEnvironment(CouchbaseConnectionDetails connectionDetails, ObjectProvider customizers) { - Builder builder = initializeEnvironmentBuilder(); + Builder builder = initializeEnvironmentBuilder(connectionDetails); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } @Bean(destroyMethod = "disconnect") @ConditionalOnMissingBean - public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment) { + public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment, + CouchbaseConnectionDetails connectionDetails) { ClusterOptions options = ClusterOptions - .clusterOptions(this.connectionDetails.getUsername(), this.connectionDetails.getPassword()) + .clusterOptions(connectionDetails.getUsername(), connectionDetails.getPassword()) .environment(couchbaseClusterEnvironment); - return Cluster.connect(this.connectionDetails.getConnectionString(), options); + return Cluster.connect(connectionDetails.getConnectionString(), options); } - private ClusterEnvironment.Builder initializeEnvironmentBuilder() { + private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseConnectionDetails connectionDetails) { ClusterEnvironment.Builder builder = ClusterEnvironment.builder(); Timeouts timeouts = this.properties.getEnv().getTimeouts(); builder.timeoutConfig((config) -> config.kvTimeout(timeouts.getKeyValue()) @@ -112,7 +115,7 @@ public class CouchbaseAutoConfiguration { builder.ioConfig((config) -> config.maxHttpConnections(io.getMaxEndpoints()) .numKvConnections(io.getMinEndpoints()) .idleHttpConnectionTimeout(io.getIdleHttpConnectionTimeout())); - if ((this.connectionDetails instanceof PropertiesCouchbaseConnectionDetails) + if ((connectionDetails instanceof PropertiesCouchbaseConnectionDetails) && this.properties.getEnv().getSsl().getEnabled()) { builder.securityConfig((config) -> config.enableTls(true) .trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl()))); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java index 06f315a16b..db1ac8876e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -21,9 +21,13 @@ import com.mongodb.client.MongoClient; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.MongoProperties; +import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.gridfs.GridFsTemplate; @@ -53,4 +57,10 @@ import org.springframework.data.mongodb.gridfs.GridFsTemplate; MongoDatabaseFactoryDependentConfiguration.class }) public class MongoDataAutoConfiguration { + @Bean + @ConditionalOnMissingBean(MongoConnectionDetails.class) + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { + return new PropertiesMongoConnectionDetails(properties); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryConfiguration.java index 17f054ce56..0c1915d12e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryConfiguration.java @@ -18,12 +18,10 @@ package org.springframework.boot.autoconfigure.data.mongo; import com.mongodb.client.MongoClient; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.MongoProperties; -import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoDatabaseFactory; @@ -45,11 +43,8 @@ class MongoDatabaseFactoryConfiguration { @Bean MongoDatabaseFactorySupport mongoDatabaseFactory(MongoClient mongoClient, MongoProperties properties, - ObjectProvider connectionDetails) { - return new SimpleMongoClientDatabaseFactory(mongoClient, - connectionDetails.getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties)) - .getConnectionString() - .getDatabase()); + MongoConnectionDetails connectionDetails) { + return new SimpleMongoClientDatabaseFactory(mongoClient, connectionDetails.getConnectionString().getDatabase()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.java index e82c6fb570..77ed0c6dc4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.java @@ -20,14 +20,12 @@ import com.mongodb.ClientSessionOptions; import com.mongodb.client.ClientSession; import com.mongodb.client.MongoDatabase; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails.GridFs; import org.springframework.boot.autoconfigure.mongo.MongoProperties; import org.springframework.boot.autoconfigure.mongo.MongoProperties.Gridfs; -import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataAccessException; @@ -76,9 +74,7 @@ class MongoDatabaseFactoryDependentConfiguration { @Bean @ConditionalOnMissingBean(GridFsOperations.class) GridFsTemplate gridFsTemplate(MongoProperties properties, MongoDatabaseFactory factory, MongoTemplate mongoTemplate, - ObjectProvider connectionDetailsProvider) { - MongoConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties)); + MongoConnectionDetails connectionDetails) { return new GridFsTemplate(new GridFsMongoDatabaseFactory(factory, connectionDetails), mongoTemplate.getConverter(), (connectionDetails.getGridFs() != null) ? connectionDetails.getGridFs().getBucket() : null); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveDataAutoConfiguration.java index 742270adc4..b2480e747e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveDataAutoConfiguration.java @@ -26,7 +26,6 @@ import org.bson.codecs.Codec; import org.bson.codecs.configuration.CodecRegistry; import reactor.core.publisher.Mono; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -36,7 +35,6 @@ import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails.GridFs; import org.springframework.boot.autoconfigure.mongo.MongoProperties; import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; -import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -80,10 +78,8 @@ public class MongoReactiveDataAutoConfiguration { private final MongoConnectionDetails connectionDetails; - MongoReactiveDataAutoConfiguration(MongoProperties properties, - ObjectProvider connectionDetails) { - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties)); + MongoReactiveDataAutoConfiguration(MongoConnectionDetails connectionDetails) { + this.connectionDetails = connectionDetails; } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java index 5110ca7293..a7d0b0f7ad 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java @@ -55,10 +55,9 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration { JedisConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfiguration, - ObjectProvider clusterConfiguration, - ObjectProvider connectionDetailsProvider) { - super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration, - connectionDetailsProvider); + ObjectProvider clusterConfiguration, RedisConnectionDetails connectionDetails) { + super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfiguration, + clusterConfiguration); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index 276c69d0da..7d21a77f63 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -64,9 +64,9 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration { ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfigurationProvider, ObjectProvider clusterConfigurationProvider, - ObjectProvider connectionDetailsProvider) { - super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider, - connectionDetailsProvider); + RedisConnectionDetails connectionDetails) { + super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfigurationProvider, + clusterConfigurationProvider); } @Bean(destroyMethod = "shutdown") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java index f4987700d6..fdef9695ff 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -49,6 +49,12 @@ import org.springframework.data.redis.core.StringRedisTemplate; @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { + @Bean + @ConditionalOnMissingBean(RedisConnectionDetails.class) + PropertiesRedisConnectionDetails redisConnectionDetails(RedisProperties properties) { + return new PropertiesRedisConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean(name = "redisTemplate") @ConditionalOnSingleCandidate(RedisConnectionFactory.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java index 9626b17557..733e063875 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java @@ -60,17 +60,15 @@ abstract class RedisConnectionConfiguration { private final RedisConnectionDetails connectionDetails; - protected RedisConnectionConfiguration(RedisProperties properties, + protected RedisConnectionConfiguration(RedisProperties properties, RedisConnectionDetails connectionDetails, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfigurationProvider, - ObjectProvider clusterConfigurationProvider, - ObjectProvider connectionDetailsProvider) { + ObjectProvider clusterConfigurationProvider) { this.properties = properties; this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable(); this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable(); this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable(); - this.connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesRedisConnectionDetails(properties)); + this.connectionDetails = connectionDetails; } protected final RedisStandaloneConfiguration getStandaloneConfig() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java index 40f945e830..193827aed8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java @@ -62,24 +62,27 @@ class ElasticsearchRestClientConfigurations { private final ElasticsearchProperties properties; - private final ElasticsearchConnectionDetails connectionDetails; - RestClientBuilderConfiguration(ElasticsearchProperties properties, ObjectProvider connectionDetails) { this.properties = properties; - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesElasticsearchConnectionDetails(properties)); } @Bean - RestClientBuilderCustomizer defaultRestClientBuilderCustomizer() { - return new DefaultRestClientBuilderCustomizer(this.properties, this.connectionDetails); + @ConditionalOnMissingBean(ElasticsearchConnectionDetails.class) + PropertiesElasticsearchConnectionDetails elasticsearchConnectionDetails() { + return new PropertiesElasticsearchConnectionDetails(this.properties); + } + + @Bean + RestClientBuilderCustomizer defaultRestClientBuilderCustomizer( + ElasticsearchConnectionDetails connectionDetails) { + return new DefaultRestClientBuilderCustomizer(this.properties, connectionDetails); } @Bean - RestClientBuilder elasticsearchRestClientBuilder( + RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchConnectionDetails connectionDetails, ObjectProvider builderCustomizers) { - RestClientBuilder builder = RestClient.builder(this.connectionDetails.getNodes() + RestClientBuilder builder = RestClient.builder(connectionDetails.getNodes() .stream() .map((node) -> new HttpHost(node.hostname(), node.port(), node.protocol().getScheme())) .toArray(HttpHost[]::new)); @@ -91,7 +94,7 @@ class ElasticsearchRestClientConfigurations { builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder)); return requestConfigBuilder; }); - String pathPrefix = this.connectionDetails.getPathPrefix(); + String pathPrefix = connectionDetails.getPathPrefix(); if (pathPrefix != null) { builder.setPathPrefix(pathPrefix); } @@ -212,7 +215,7 @@ class ElasticsearchRestClientConfigurations { /** * Adapts {@link ElasticsearchProperties} to {@link ElasticsearchConnectionDetails}. */ - private static class PropertiesElasticsearchConnectionDetails implements ElasticsearchConnectionDetails { + static class PropertiesElasticsearchConnectionDetails implements ElasticsearchConnectionDetails { private final ElasticsearchProperties properties; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index c0bc7bc171..ce27883c6d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import javax.sql.DataSource; @@ -121,27 +122,31 @@ public class FlywayAutoConfiguration { return new ResourceProviderCustomizer(); } + @Bean + @ConditionalOnMissingBean(FlywayConnectionDetails.class) + PropertiesFlywayConnectionDetails flywayConnectionDetails(FlywayProperties properties, + ObjectProvider jdbcConnectionDetails) { + return new PropertiesFlywayConnectionDetails(properties, jdbcConnectionDetails.getIfAvailable()); + } + @Deprecated(since = "3.0.0", forRemoval = true) public Flyway flyway(FlywayProperties properties, ResourceLoader resourceLoader, ObjectProvider dataSource, ObjectProvider flywayDataSource, ObjectProvider fluentConfigurationCustomizers, ObjectProvider javaMigrations, ObjectProvider callbacks) { - return flyway(properties, resourceLoader, dataSource, flywayDataSource, fluentConfigurationCustomizers, - javaMigrations, callbacks, new ResourceProviderCustomizer(), null); + return flyway(properties, new PropertiesFlywayConnectionDetails(properties, null), resourceLoader, + dataSource, flywayDataSource, fluentConfigurationCustomizers, javaMigrations, callbacks, + new ResourceProviderCustomizer()); } @Bean - Flyway flyway(FlywayProperties properties, ResourceLoader resourceLoader, ObjectProvider dataSource, + Flyway flyway(FlywayProperties properties, FlywayConnectionDetails connectionDetails, + ResourceLoader resourceLoader, ObjectProvider dataSource, @FlywayDataSource ObjectProvider flywayDataSource, ObjectProvider fluentConfigurationCustomizers, ObjectProvider javaMigrations, ObjectProvider callbacks, - ResourceProviderCustomizer resourceProviderCustomizer, - ObjectProvider connectionDetailsProvider) { + ResourceProviderCustomizer resourceProviderCustomizer) { FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader()); - JdbcConnectionDetails connectionDetails = (connectionDetailsProvider != null) - ? connectionDetailsProvider.getIfAvailable() : null; - connectionDetails = (connectionDetails != null) ? connectionDetails - : new FlywayPropertiesJdbcConnectionDetails(properties); configureDataSource(configuration, flywayDataSource.getIfAvailable(), dataSource.getIfUnique(), connectionDetails); configureProperties(configuration, properties); @@ -153,13 +158,13 @@ public class FlywayAutoConfiguration { } private void configureDataSource(FluentConfiguration configuration, DataSource flywayDataSource, - DataSource dataSource, JdbcConnectionDetails connectionDetails) { + DataSource dataSource, FlywayConnectionDetails connectionDetails) { DataSource migrationDataSource = getMigrationDataSource(flywayDataSource, dataSource, connectionDetails); configuration.dataSource(migrationDataSource); } private DataSource getMigrationDataSource(DataSource flywayDataSource, DataSource dataSource, - JdbcConnectionDetails connectionDetails) { + FlywayConnectionDetails connectionDetails) { if (flywayDataSource != null) { return flywayDataSource; } @@ -181,7 +186,7 @@ public class FlywayAutoConfiguration { return dataSource; } - private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder builder) { + private void applyConnectionDetails(FlywayConnectionDetails connectionDetails, DataSourceBuilder builder) { builder.username(connectionDetails.getUsername()); builder.password(connectionDetails.getPassword()); String driverClassName = connectionDetails.getDriverClassName(); @@ -406,34 +411,46 @@ public class FlywayAutoConfiguration { } /** - * Adapts {@link FlywayProperties} to {@link JdbcConnectionDetails}. + * Adapts {@link FlywayProperties} to {@link FlywayConnectionDetails}, using + * {@link JdbcConnectionDetails} as a fallback when Flyway-specific properties have + * not be configured. */ - private static final class FlywayPropertiesJdbcConnectionDetails implements JdbcConnectionDetails { + static final class PropertiesFlywayConnectionDetails implements FlywayConnectionDetails { + + private final JdbcConnectionDetails fallback; private final FlywayProperties properties; - private FlywayPropertiesJdbcConnectionDetails(FlywayProperties properties) { + PropertiesFlywayConnectionDetails(FlywayProperties properties, JdbcConnectionDetails fallback) { + this.fallback = fallback; this.properties = properties; } @Override public String getUsername() { - return this.properties.getUser(); + return get(this.properties.getUser(), JdbcConnectionDetails::getUsername); } @Override public String getPassword() { - return this.properties.getPassword(); + return get(this.properties.getPassword(), JdbcConnectionDetails::getPassword); } @Override public String getJdbcUrl() { - return this.properties.getUrl(); + return get(this.properties.getUrl(), JdbcConnectionDetails::getJdbcUrl); } @Override public String getDriverClassName() { - return this.properties.getDriverClassName(); + return get(this.properties.getDriverClassName(), JdbcConnectionDetails::getDriverClassName); + } + + private String get(String primary, Function fallbackProperty) { + if (primary != null) { + return primary; + } + return (this.fallback != null) ? fallbackProperty.apply(this.fallback) : null; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayConnectionDetails.java new file mode 100644 index 0000000000..c6f6487592 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayConnectionDetails.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2023 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 org.springframework.boot.autoconfigure.flyway; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.jdbc.DatabaseDriver; + +/** + * Details required for Flyway to establish a connection to an SQL service using JDBC. + * + * @author Andy Wilkinson + * @since 3.1.0 + */ +public interface FlywayConnectionDetails extends ConnectionDetails { + + /** + * Username for the database. + * @return the username for the database + */ + String getUsername(); + + /** + * Password for the database. + * @return the password for the database + */ + String getPassword(); + + /** + * JDBC URL for the database. + * @return the JDBC URL for the database + */ + String getJdbcUrl(); + + /** + * The name of the JDBC driver class. Defaults to the class name of the driver + * specified in the JDBC URL. + * @return the JDBC driver class name + * @see #getJdbcUrl() + * @see DatabaseDriver#fromJdbcUrl(String) + * @see DatabaseDriver#getDriverClassName() + */ + default String getDriverClassName() { + return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getDriverClassName(); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfiguration.java index d793ff83dd..9fb7c9a921 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfiguration.java @@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.influx.InfluxDbAutoConfiguration.InfluxDBCondition; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; @@ -48,17 +49,21 @@ import org.springframework.context.annotation.Conditional; */ @AutoConfiguration @ConditionalOnClass(InfluxDB.class) +@Conditional(InfluxDBCondition.class) @EnableConfigurationProperties(InfluxDbProperties.class) public class InfluxDbAutoConfiguration { + @Bean + @ConditionalOnMissingBean(InfluxDbConnectionDetails.class) + PropertiesInfluxDbConnectionDetails influxDbConnectionDetails(InfluxDbProperties properties) { + return new PropertiesInfluxDbConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean - @Conditional(InfluxDBCondition.class) - public InfluxDB influxDb(InfluxDbProperties properties, ObjectProvider builder, - ObjectProvider customizers, - ObjectProvider connectionDetailsProvider) { - InfluxDbConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesInfluxDbConnectionDetails(properties)); + public InfluxDB influxDb(InfluxDbConnectionDetails connectionDetails, + ObjectProvider builder, + ObjectProvider customizers) { InfluxDB influxDb = new InfluxDBImpl(connectionDetails.getUrl().toString(), connectionDetails.getUsername(), connectionDetails.getPassword(), determineBuilder(builder.getIfAvailable())); customizers.orderedStream().forEach((customizer) -> customizer.customize(influxDb)); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 56a881a380..5a41f5d0bd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConf import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; @@ -75,6 +76,12 @@ public class DataSourceAutoConfiguration { DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { + @Bean + @ConditionalOnMissingBean(JdbcConnectionDetails.class) + PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) { + return new PropertiesJdbcConnectionDetails(properties); + } + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java index 61dd0e53ab..8dd321ee7c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java @@ -25,7 +25,6 @@ import oracle.jdbc.OracleConnection; import oracle.ucp.jdbc.PoolDataSourceImpl; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -48,20 +47,15 @@ import org.springframework.util.StringUtils; */ abstract class DataSourceConfiguration { - @SuppressWarnings("unchecked") - private static T createDataSource(DataSourceProperties properties, Class type) { - return (T) properties.initializeDataSourceBuilder().type(type).build(); - } - @SuppressWarnings("unchecked") private static T createDataSource(JdbcConnectionDetails connectionDetails, Class type, ClassLoader classLoader) { return (T) DataSourceBuilder.create(classLoader) + .type(type) + .driverClassName(connectionDetails.getDriverClassName()) .url(connectionDetails.getJdbcUrl()) .username(connectionDetails.getUsername()) .password(connectionDetails.getPassword()) - .driverClassName(connectionDetails.getDriverClassName()) - .type(type) .build(); } @@ -76,7 +70,7 @@ abstract class DataSourceConfiguration { static class Tomcat { @Bean - @ConditionalOnBean(JdbcConnectionDetails.class) + @ConditionalOnMissingBean(PropertiesJdbcConnectionDetails.class) static TomcatJdbcConnectionDetailsBeanPostProcessor tomcatJdbcConnectionDetailsBeanPostProcessor( ObjectProvider connectionDetailsProvider) { return new TomcatJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider); @@ -85,15 +79,12 @@ abstract class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.tomcat") org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties, - ObjectProvider connectionDetailsProvider) { - JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable(); + JdbcConnectionDetails connectionDetails) { Class dataSourceType = org.apache.tomcat.jdbc.pool.DataSource.class; - org.apache.tomcat.jdbc.pool.DataSource dataSource = (connectionDetails != null) - ? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader()) - : createDataSource(properties, dataSourceType); + org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(connectionDetails, dataSourceType, + properties.getClassLoader()); String validationQuery; - String url = (connectionDetails != null) ? connectionDetails.getJdbcUrl() : properties.determineUrl(); - DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(url); + DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(connectionDetails.getJdbcUrl()); validationQuery = databaseDriver.getValidationQuery(); if (validationQuery != null) { dataSource.setTestOnBorrow(true); @@ -115,7 +106,6 @@ abstract class DataSourceConfiguration { static class Hikari { @Bean - @ConditionalOnBean(JdbcConnectionDetails.class) static HikariJdbcConnectionDetailsBeanPostProcessor jdbcConnectionDetailsHikariBeanPostProcessor( ObjectProvider connectionDetailsProvider) { return new HikariJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider); @@ -123,12 +113,9 @@ abstract class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") - HikariDataSource dataSource(DataSourceProperties properties, - ObjectProvider connectionDetailsProvider) { - JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable(); - HikariDataSource dataSource = (connectionDetails != null) - ? createDataSource(connectionDetails, HikariDataSource.class, properties.getClassLoader()) - : createDataSource(properties, HikariDataSource.class); + HikariDataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) { + HikariDataSource dataSource = createDataSource(connectionDetails, HikariDataSource.class, + properties.getClassLoader()); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } @@ -148,7 +135,6 @@ abstract class DataSourceConfiguration { static class Dbcp2 { @Bean - @ConditionalOnBean(JdbcConnectionDetails.class) static Dbcp2JdbcConnectionDetailsBeanPostProcessor dbcp2JdbcConnectionDetailsBeanPostProcessor( ObjectProvider connectionDetailsProvider) { return new Dbcp2JdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider); @@ -157,12 +143,9 @@ abstract class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.dbcp2") org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties, - ObjectProvider connectionDetailsProvider) { - JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable(); + JdbcConnectionDetails connectionDetails) { Class dataSourceType = org.apache.commons.dbcp2.BasicDataSource.class; - return (connectionDetails != null) - ? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader()) - : createDataSource(properties, dataSourceType); + return createDataSource(connectionDetails, dataSourceType, properties.getClassLoader()); } } @@ -178,7 +161,6 @@ abstract class DataSourceConfiguration { static class OracleUcp { @Bean - @ConditionalOnBean(JdbcConnectionDetails.class) static OracleUcpJdbcConnectionDetailsBeanPostProcessor oracleUcpJdbcConnectionDetailsBeanPostProcessor( ObjectProvider connectionDetailsProvider) { return new OracleUcpJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider); @@ -186,12 +168,10 @@ abstract class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.oracleucp") - PoolDataSourceImpl dataSource(DataSourceProperties properties, - ObjectProvider connectionDetailsProvider) throws SQLException { - JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable(); - PoolDataSourceImpl dataSource = (connectionDetails != null) - ? createDataSource(connectionDetails, PoolDataSourceImpl.class, properties.getClassLoader()) - : createDataSource(properties, PoolDataSourceImpl.class); + PoolDataSourceImpl dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) + throws SQLException { + PoolDataSourceImpl dataSource = createDataSource(connectionDetails, PoolDataSourceImpl.class, + properties.getClassLoader()); dataSource.setValidateConnectionOnBorrow(true); if (StringUtils.hasText(properties.getName())) { dataSource.setConnectionPoolName(properties.getName()); @@ -210,13 +190,8 @@ abstract class DataSourceConfiguration { static class Generic { @Bean - DataSource dataSource(DataSourceProperties properties, - ObjectProvider connectionDetailsProvider) { - JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable(); - if (connectionDetails != null) { - return createDataSource(connectionDetails, properties.getType(), properties.getClassLoader()); - } - return properties.initializeDataSourceBuilder().build(); + DataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) { + return createDataSource(connectionDetails, properties.getType(), properties.getClassLoader()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetails.java index f6ca3c4afe..e621f42a29 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetails.java @@ -30,7 +30,7 @@ import org.springframework.boot.jdbc.DatabaseDriver; public interface JdbcConnectionDetails extends ConnectionDetails { /** - * Hostname for the database. + * Username for the database. * @return the username for the database */ String getUsername(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor.java index e4cb0c8acf..ed3d13b9d9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor.java @@ -24,7 +24,10 @@ import org.springframework.core.PriorityOrdered; /** * Abstract base class for DataSource bean post processors which apply values from - * {@link JdbcConnectionDetails}. Acts on beans named 'dataSource' of type {@code T}. + * {@link JdbcConnectionDetails}. Property-based connection details + * ({@link PropertiesJdbcConnectionDetails} are ignored as the expectation is that they + * will have already been applied by configuration property binding. Acts on beans named + * 'dataSource' of type {@code T}. * * @param type of the datasource * @author Moritz Halbritter @@ -48,7 +51,9 @@ abstract class JdbcConnectionDetailsBeanPostProcessor implements BeanPostProc public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (this.dataSourceClass.isAssignableFrom(bean.getClass()) && "dataSource".equals(beanName)) { JdbcConnectionDetails connectionDetails = this.connectionDetailsProvider.getObject(); - return processDataSource((T) bean, connectionDetails); + if (!(connectionDetails instanceof PropertiesJdbcConnectionDetails)) { + return processDataSource((T) bean, connectionDetails); + } } return bean; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/PropertiesJdbcConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/PropertiesJdbcConnectionDetails.java new file mode 100644 index 0000000000..40109d89aa --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/PropertiesJdbcConnectionDetails.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2023 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 org.springframework.boot.autoconfigure.jdbc; + +/** + * Adapts {@link DataSourceProperties} to {@link JdbcConnectionDetails}. + * + * @author Andy Wilkinson + */ +final class PropertiesJdbcConnectionDetails implements JdbcConnectionDetails { + + private final DataSourceProperties properties; + + PropertiesJdbcConnectionDetails(DataSourceProperties properties) { + this.properties = properties; + } + + @Override + public String getUsername() { + return this.properties.determineUsername(); + } + + @Override + public String getPassword() { + return this.properties.determinePassword(); + } + + @Override + public String getJdbcUrl() { + return this.properties.determineUrl(); + } + + @Override + public String getDriverClassName() { + return this.properties.determineDriverClassName(); + } + + @Override + public String getXaDataSourceClassName() { + return (this.properties.getXa().getDataSourceClassName() != null) + ? this.properties.getXa().getDataSourceClassName() + : JdbcConnectionDetails.super.getXaDataSourceClassName(); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java index 46a9f60f92..b78ec02c7f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java @@ -66,12 +66,17 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware { private ClassLoader classLoader; + @Bean + @ConditionalOnMissingBean(JdbcConnectionDetails.class) + PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) { + return new PropertiesJdbcConnectionDetails(properties); + } + @Bean public DataSource dataSource(XADataSourceWrapper wrapper, DataSourceProperties properties, - ObjectProvider connectionDetails, ObjectProvider xaDataSource) - throws Exception { - return wrapper.wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties, - connectionDetails.getIfAvailable(() -> new PropertiesJdbcConnectionDetails(properties))))); + JdbcConnectionDetails connectionDetails, ObjectProvider xaDataSource) throws Exception { + return wrapper + .wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties, connectionDetails))); } @Override @@ -122,45 +127,4 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware { return source.withAliases(aliases); } - /** - * Adapts {@link DataSourceProperties} to {@link JdbcConnectionDetails}. - */ - private static class PropertiesJdbcConnectionDetails implements JdbcConnectionDetails { - - private final DataSourceProperties properties; - - PropertiesJdbcConnectionDetails(DataSourceProperties properties) { - this.properties = properties; - } - - @Override - public String getUsername() { - return this.properties.determineUsername(); - } - - @Override - public String getPassword() { - return this.properties.determinePassword(); - } - - @Override - public String getJdbcUrl() { - return this.properties.determineUrl(); - } - - @Override - public String getDriverClassName() { - return (this.properties.getDriverClassName() != null) ? this.properties.getDriverClassName() - : JdbcConnectionDetails.super.getDriverClassName(); - } - - @Override - public String getXaDataSourceClassName() { - return (this.properties.getXa().getDataSourceClassName() != null) - ? this.properties.getXa().getDataSourceClassName() - : JdbcConnectionDetails.super.getXaDataSourceClassName(); - } - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java index 9b9632ddc9..5a4e430753 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java @@ -76,12 +76,14 @@ public class KafkaAutoConfiguration { private final KafkaProperties properties; - private final KafkaConnectionDetails connectionDetails; - - KafkaAutoConfiguration(KafkaProperties properties, ObjectProvider connectionDetails) { + KafkaAutoConfiguration(KafkaProperties properties) { this.properties = properties; - this.connectionDetails = connectionDetails - .getIfAvailable(() -> new PropertiesKafkaConnectionDetails(properties)); + } + + @Bean + @ConditionalOnMissingBean(KafkaConnectionDetails.class) + PropertiesKafkaConnectionDetails kafkaConnectionDetails(KafkaProperties properties) { + return new PropertiesKafkaConnectionDetails(properties); } @Bean @@ -106,10 +108,10 @@ public class KafkaAutoConfiguration { @Bean @ConditionalOnMissingBean(ConsumerFactory.class) - public DefaultKafkaConsumerFactory kafkaConsumerFactory( + public DefaultKafkaConsumerFactory kafkaConsumerFactory(KafkaConnectionDetails connectionDetails, ObjectProvider customizers) { Map properties = this.properties.buildConsumerProperties(); - applyKafkaConnectionDetailsForConsumer(properties); + applyKafkaConnectionDetailsForConsumer(properties, connectionDetails); DefaultKafkaConsumerFactory factory = new DefaultKafkaConsumerFactory<>(properties); customizers.orderedStream().forEach((customizer) -> customizer.customize(factory)); return factory; @@ -117,10 +119,10 @@ public class KafkaAutoConfiguration { @Bean @ConditionalOnMissingBean(ProducerFactory.class) - public DefaultKafkaProducerFactory kafkaProducerFactory( + public DefaultKafkaProducerFactory kafkaProducerFactory(KafkaConnectionDetails connectionDetails, ObjectProvider customizers) { Map properties = this.properties.buildProducerProperties(); - applyKafkaConnectionDetailsForProducer(properties); + applyKafkaConnectionDetailsForProducer(properties, connectionDetails); DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory<>(properties); String transactionIdPrefix = this.properties.getProducer().getTransactionIdPrefix(); if (transactionIdPrefix != null) { @@ -155,9 +157,9 @@ public class KafkaAutoConfiguration { @Bean @ConditionalOnMissingBean - public KafkaAdmin kafkaAdmin() { + public KafkaAdmin kafkaAdmin(KafkaConnectionDetails connectionDetails) { Map properties = this.properties.buildAdminProperties(); - applyKafkaConnectionDetailsForAdmin(properties); + applyKafkaConnectionDetailsForAdmin(properties, connectionDetails); KafkaAdmin kafkaAdmin = new KafkaAdmin(properties); KafkaProperties.Admin admin = this.properties.getAdmin(); if (admin.getCloseTimeout() != null) { @@ -186,26 +188,29 @@ public class KafkaAutoConfiguration { return builder.create(kafkaTemplate); } - private void applyKafkaConnectionDetailsForConsumer(Map properties) { + private void applyKafkaConnectionDetailsForConsumer(Map properties, + KafkaConnectionDetails connectionDetails) { properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, - nodesToStringList(this.connectionDetails.getConsumerBootstrapNodes())); - if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) { + nodesToStringList(connectionDetails.getConsumerBootstrapNodes())); + if (!(connectionDetails instanceof PropertiesKafkaConnectionDetails)) { properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT"); } } - private void applyKafkaConnectionDetailsForProducer(Map properties) { + private void applyKafkaConnectionDetailsForProducer(Map properties, + KafkaConnectionDetails connectionDetails) { properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, - nodesToStringList(this.connectionDetails.getProducerBootstrapNodes())); - if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) { + nodesToStringList(connectionDetails.getProducerBootstrapNodes())); + if (!(connectionDetails instanceof PropertiesKafkaConnectionDetails)) { properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT"); } } - private void applyKafkaConnectionDetailsForAdmin(Map properties) { + private void applyKafkaConnectionDetailsForAdmin(Map properties, + KafkaConnectionDetails connectionDetails) { properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, - nodesToStringList(this.connectionDetails.getAdminBootstrapNodes())); - if (!(this.connectionDetails instanceof PropertiesKafkaConnectionDetails)) { + nodesToStringList(connectionDetails.getAdminBootstrapNodes())); + if (!(connectionDetails instanceof PropertiesKafkaConnectionDetails)) { properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT"); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaStreamsAnnotationDrivenConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaStreamsAnnotationDrivenConfiguration.java index a3ae685e7f..5f425c6d1f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaStreamsAnnotationDrivenConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaStreamsAnnotationDrivenConfiguration.java @@ -63,11 +63,9 @@ class KafkaStreamsAnnotationDrivenConfiguration { @ConditionalOnMissingBean @Bean(KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME) KafkaStreamsConfiguration defaultKafkaStreamsConfig(Environment environment, - ObjectProvider connectionDetailsProvider) { - KafkaConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesKafkaConnectionDetails(this.properties)); + KafkaConnectionDetails connectionDetails) { Map properties = this.properties.buildStreamsProperties(); - applyKafkaConnectionDetailsForStreams(connectionDetails, properties); + applyKafkaConnectionDetailsForStreams(properties, connectionDetails); if (this.properties.getStreams().getApplicationId() == null) { String applicationName = environment.getProperty("spring.application.name"); if (applicationName == null) { @@ -87,8 +85,8 @@ class KafkaStreamsAnnotationDrivenConfiguration { return new KafkaStreamsFactoryBeanConfigurer(this.properties, factoryBean); } - private void applyKafkaConnectionDetailsForStreams(KafkaConnectionDetails connectionDetails, - Map properties) { + private void applyKafkaConnectionDetailsForStreams(Map properties, + KafkaConnectionDetails connectionDetails) { properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, nodesToStringList(connectionDetails.getStreamsBootstrapNodes())); if (!(connectionDetails instanceof PropertiesKafkaConnectionDetails)) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index d3e08a691a..fed79292fc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.liquibase; +import java.util.function.Function; + import javax.sql.DataSource; import liquibase.change.DatabaseChange; @@ -31,6 +33,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.flyway.FlywayConnectionDetails; +import org.springframework.boot.autoconfigure.flyway.FlywayProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints; @@ -85,13 +89,19 @@ public class LiquibaseAutoConfiguration { @EnableConfigurationProperties(LiquibaseProperties.class) public static class LiquibaseConfiguration { + @Bean + @ConditionalOnMissingBean(LiquibaseConnectionDetails.class) + PropertiesLiquibaseConnectionDetails liquibaseConnectionDetails(LiquibaseProperties properties, + ObjectProvider jdbcConnectionDetails) { + return new PropertiesLiquibaseConnectionDetails(properties, jdbcConnectionDetails.getIfAvailable()); + } + @Bean public SpringLiquibase liquibase(ObjectProvider dataSource, @LiquibaseDataSource ObjectProvider liquibaseDataSource, LiquibaseProperties properties, - ObjectProvider connectionDetails) { + LiquibaseConnectionDetails connectionDetails) { SpringLiquibase liquibase = createSpringLiquibase(liquibaseDataSource.getIfAvailable(), - dataSource.getIfUnique(), - connectionDetails.getIfAvailable(() -> new LiquibasePropertiesJdbcConnectionDetails(properties))); + dataSource.getIfUnique(), connectionDetails); liquibase.setChangeLog(properties.getChangeLog()); liquibase.setClearCheckSums(properties.isClearChecksums()); liquibase.setContexts(properties.getContexts()); @@ -111,7 +121,7 @@ public class LiquibaseAutoConfiguration { } private SpringLiquibase createSpringLiquibase(DataSource liquibaseDataSource, DataSource dataSource, - JdbcConnectionDetails connectionDetails) { + LiquibaseConnectionDetails connectionDetails) { DataSource migrationDataSource = getMigrationDataSource(liquibaseDataSource, dataSource, connectionDetails); SpringLiquibase liquibase = (migrationDataSource == liquibaseDataSource || migrationDataSource == dataSource) ? new SpringLiquibase() @@ -121,7 +131,7 @@ public class LiquibaseAutoConfiguration { } private DataSource getMigrationDataSource(DataSource liquibaseDataSource, DataSource dataSource, - JdbcConnectionDetails connectionDetails) { + LiquibaseConnectionDetails connectionDetails) { if (liquibaseDataSource != null) { return liquibaseDataSource; } @@ -143,7 +153,8 @@ public class LiquibaseAutoConfiguration { return dataSource; } - private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder builder) { + private void applyConnectionDetails(LiquibaseConnectionDetails connectionDetails, + DataSourceBuilder builder) { builder.username(connectionDetails.getUsername()); builder.password(connectionDetails.getPassword()); String driverClassName = connectionDetails.getDriverClassName(); @@ -187,34 +198,46 @@ public class LiquibaseAutoConfiguration { } /** - * Adapts {@link LiquibaseProperties} to {@link JdbcConnectionDetails}. + * Adapts {@link FlywayProperties} to {@link FlywayConnectionDetails}, using + * {@link JdbcConnectionDetails} as a fallback when Flyway-specific properties have + * not be configured. */ - private static final class LiquibasePropertiesJdbcConnectionDetails implements JdbcConnectionDetails { + static final class PropertiesLiquibaseConnectionDetails implements LiquibaseConnectionDetails { + + private final JdbcConnectionDetails fallback; private final LiquibaseProperties properties; - private LiquibasePropertiesJdbcConnectionDetails(LiquibaseProperties properties) { + PropertiesLiquibaseConnectionDetails(LiquibaseProperties properties, JdbcConnectionDetails fallback) { + this.fallback = fallback; this.properties = properties; } @Override public String getUsername() { - return this.properties.getUser(); + return get(this.properties.getUser(), JdbcConnectionDetails::getUsername); } @Override public String getPassword() { - return this.properties.getPassword(); + return get(this.properties.getPassword(), JdbcConnectionDetails::getPassword); } @Override public String getJdbcUrl() { - return this.properties.getUrl(); + return get(this.properties.getUrl(), JdbcConnectionDetails::getJdbcUrl); } @Override public String getDriverClassName() { - return this.properties.getDriverClassName(); + return get(this.properties.getDriverClassName(), JdbcConnectionDetails::getDriverClassName); + } + + private String get(String primary, Function fallbackProperty) { + if (primary != null) { + return primary; + } + return (this.fallback != null) ? fallbackProperty.apply(this.fallback) : null; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseConnectionDetails.java new file mode 100644 index 0000000000..15a88d247c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseConnectionDetails.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2023 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 org.springframework.boot.autoconfigure.liquibase; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.jdbc.DatabaseDriver; + +/** + * Details required for Liquibase to establish a connection to an SQL service using JDBC. + * + * @author Andy Wilkinson + * @since 3.1.0 + */ +public interface LiquibaseConnectionDetails extends ConnectionDetails { + + /** + * Username for the database. + * @return the username for the database + */ + String getUsername(); + + /** + * Password for the database. + * @return the password for the database + */ + String getPassword(); + + /** + * JDBC URL for the database. + * @return the JDBC URL for the database + */ + String getJdbcUrl(); + + /** + * The name of the JDBC driver class. Defaults to the class name of the driver + * specified in the JDBC URL. + * @return the JDBC driver class name + * @see #getJdbcUrl() + * @see DatabaseDriver#fromJdbcUrl(String) + * @see DatabaseDriver#getDriverClassName() + */ + default String getDriverClassName() { + return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getDriverClassName(); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java index 8d3ce82282..5ba3588a0d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java @@ -45,6 +45,12 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDatabaseFactory") public class MongoAutoConfiguration { + @Bean + @ConditionalOnMissingBean(MongoConnectionDetails.class) + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { + return new PropertiesMongoConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean(MongoClient.class) public MongoClient mongo(ObjectProvider builderCustomizers, @@ -63,9 +69,7 @@ public class MongoAutoConfiguration { @Bean StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, - ObjectProvider connectionDetailsProvider) { - MongoConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties)); + MongoConnectionDetails connectionDetails) { return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), properties.getUuidRepresentation()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java index 9d877159b1..12dcd8ef29 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java @@ -50,6 +50,12 @@ import org.springframework.core.annotation.Order; @EnableConfigurationProperties(MongoProperties.class) public class MongoReactiveAutoConfiguration { + @Bean + @ConditionalOnMissingBean(MongoConnectionDetails.class) + PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) { + return new PropertiesMongoConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean public MongoClient reactiveStreamsMongoClient( @@ -70,9 +76,7 @@ public class MongoReactiveAutoConfiguration { @Bean StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, - ObjectProvider connectionDetailsProvider) { - MongoConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesMongoConnectionDetails(properties)); + MongoConnectionDetails connectionDetails) { return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), properties.getUuidRepresentation()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java index 39b0d334f4..a7580754f8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java @@ -61,13 +61,17 @@ import org.springframework.util.StringUtils; @EnableConfigurationProperties(Neo4jProperties.class) public class Neo4jAutoConfiguration { + @Bean + @ConditionalOnMissingBean(Neo4jConnectionDetails.class) + PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties) { + return new PropertiesNeo4jConnectionDetails(properties); + } + @Bean @ConditionalOnMissingBean public Driver neo4jDriver(Neo4jProperties properties, Environment environment, - ObjectProvider configBuilderCustomizers, - ObjectProvider connectionDetailsProvider) { - Neo4jConnectionDetails connectionDetails = connectionDetailsProvider - .getIfAvailable(() -> new PropertiesNeo4jConnectionDetails(properties)); + Neo4jConnectionDetails connectionDetails, + ObjectProvider configBuilderCustomizers) { AuthToken authToken = connectionDetails.getAuthToken(); Config config = mapDriverConfig(properties, connectionDetails, configBuilderCustomizers.orderedStream().toList()); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java index 6c3352c96a..a53b482e60 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java @@ -172,14 +172,21 @@ class RabbitAutoConfigurationTests { }); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesRabbitConnectionDetails.class)); + } + @Test @SuppressWarnings("unchecked") - void testConnectionFactoryWithOverridesWhenUsingConnectionDetails() { + void testConnectionFactoryWithOverridesWhenUsingCustomConnectionDetails() { this.contextRunner.withUserConfiguration(TestConfiguration.class, ConnectionDetailsConfiguration.class) .withPropertyValues("spring.rabbitmq.host:remote-server", "spring.rabbitmq.port:9000", "spring.rabbitmq.username:alice", "spring.rabbitmq.password:secret", "spring.rabbitmq.virtual_host:/vhost") .run((context) -> { + assertThat(context).hasSingleBean(RabbitConnectionDetails.class) + .doesNotHaveBean(PropertiesRabbitConnectionDetails.class); CachingConnectionFactory connectionFactory = context.getBean(CachingConnectionFactory.class); assertThat(connectionFactory.getHost()).isEqualTo("rabbit.example.com"); assertThat(connectionFactory.getPort()).isEqualTo(12345); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java index e1c1c5edc6..31ef493afb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java @@ -33,6 +33,7 @@ import com.datastax.oss.driver.internal.core.session.throttling.RateLimitingRequ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration.PropertiesCassandraConnectionDetails; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -95,13 +96,21 @@ class CassandraAutoConfigurationTests { } @Test - void shouldUseConnectionDetails() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner + .run((context) -> assertThat(context).hasSingleBean(PropertiesCassandraConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { this.contextRunner .withPropertyValues("spring.cassandra.contact-points=localhost:9042", "spring.cassandra.username=a-user", "spring.cassandra.password=a-password", "spring.cassandra.local-datacenter=some-datacenter") .withBean(CassandraConnectionDetails.class, this::cassandraConnectionDetails) .run((context) -> { - assertThat(context).hasSingleBean(DriverConfigLoader.class); + assertThat(context).hasSingleBean(DriverConfigLoader.class) + .hasSingleBean(CassandraConnectionDetails.class) + .doesNotHaveBean(PropertiesCassandraConnectionDetails.class); DriverExecutionProfile configuration = context.getBean(DriverConfigLoader.class) .getInitialConfig() .getDefaultProfile(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java index b02e6ffb4a..2381016fc7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfigurationTests.java @@ -34,6 +34,7 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.PropertiesCouchbaseConnectionDetails; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -64,10 +65,20 @@ class CouchbaseAutoConfigurationTests { } @Test - void shouldUseConnectionDetails() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.withUserConfiguration(CouchbaseTestConfiguration.class) + .withPropertyValues("spring.couchbase.connection-string=localhost") + .run((context) -> assertThat(context).hasSingleBean(PropertiesCouchbaseConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { this.contextRunner.withBean(CouchbaseConnectionDetails.class, this::couchbaseConnectionDetails) .run((context) -> { - assertThat(context).hasSingleBean(ClusterEnvironment.class).hasSingleBean(Cluster.class); + assertThat(context).hasSingleBean(ClusterEnvironment.class) + .hasSingleBean(Cluster.class) + .hasSingleBean(CouchbaseConnectionDetails.class) + .doesNotHaveBean(PropertiesCouchbaseConnectionDetails.class); Cluster cluster = context.getBean(Cluster.class); assertThat(cluster.core()).extracting("connectionString.hosts") .asList() diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java index 6f75319a6e..50941bf7f3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java @@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.data.mongo.country.Country; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; +import org.springframework.boot.autoconfigure.mongo.PropertiesMongoConnectionDetails; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -225,6 +226,25 @@ class MongoDataAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(MongoTemplate.class)); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesMongoConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { + this.contextRunner.withBean(MongoConnectionDetails.class, () -> new MongoConnectionDetails() { + + @Override + public ConnectionString getConnectionString() { + return new ConnectionString("mongodb://localhost/testdb"); + } + + }) + .run((context) -> assertThat(context).hasSingleBean(MongoConnectionDetails.class) + .doesNotHaveBean(PropertiesMongoConnectionDetails.class)); + } + private static void assertDomainTypesDiscovered(MongoMappingContext mappingContext, Class... types) { ManagedTypes managedTypes = (ManagedTypes) ReflectionTestUtils.getField(mappingContext, "managedTypes"); assertThat(managedTypes.toList()).containsOnly(types); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index fa1bfbc6ee..2b4a14d2e1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -495,8 +495,15 @@ class RedisAutoConfigurationTests { } @Test - void usesStandaloneFromConnectionDetailsIfAvailable() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesRedisConnectionDetails.class)); + } + + @Test + void usesStandaloneFromCustomConnectionDetails() { this.contextRunner.withUserConfiguration(ConnectionDetailsStandaloneConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(RedisConnectionDetails.class) + .doesNotHaveBean(PropertiesRedisConnectionDetails.class); LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(cf.isUseSsl()).isFalse(); RedisStandaloneConfiguration configuration = cf.getStandaloneConfiguration(); @@ -509,8 +516,10 @@ class RedisAutoConfigurationTests { } @Test - void usesSentinelFromConnectionDetailsIfAvailable() { + void usesSentinelFromCustomConnectionDetails() { this.contextRunner.withUserConfiguration(ConnectionDetailsSentinelConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(RedisConnectionDetails.class) + .doesNotHaveBean(PropertiesRedisConnectionDetails.class); LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(cf.isUseSsl()).isFalse(); RedisSentinelConfiguration configuration = cf.getSentinelConfiguration(); @@ -526,8 +535,10 @@ class RedisAutoConfigurationTests { } @Test - void usesClusterFromConnectionDetailsIfAvailable() { + void usesClusterFromCustomConnectionDetails() { this.contextRunner.withUserConfiguration(ConnectionDetailsClusterConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(RedisConnectionDetails.class) + .doesNotHaveBean(PropertiesRedisConnectionDetails.class); LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(cf.isUseSsl()).isFalse(); RedisClusterConfiguration configuration = cf.getClusterConfiguration(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java index 6e57d8112b..18d1d1e28e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.PropertiesElasticsearchConnectionDetails; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -248,9 +249,17 @@ class ElasticsearchRestClientAutoConfigurationTests { } @Test - void connectionDetailsAreUsedIfAvailable() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner + .run((context) -> assertThat(context).hasSingleBean(PropertiesElasticsearchConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(RestClient.class); + assertThat(context).hasSingleBean(RestClient.class) + .hasSingleBean(ElasticsearchConnectionDetails.class) + .doesNotHaveBean(PropertiesElasticsearchConnectionDetails.class); RestClient restClient = context.getBean(RestClient.class); assertThat(restClient).hasFieldOrPropertyWithValue("pathPrefix", "/some-path"); assertThat(restClient.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index d904bc9be6..cb052c8343 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -40,6 +40,7 @@ import org.jooq.impl.DefaultDSLContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InOrder; +import org.postgresql.Driver; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; @@ -149,13 +150,34 @@ class FlywayAutoConfigurationTests { } @Test - void createDataSourceWithJdbcConnectionDetails() { + void flywayPropertiesAreUsedOverJdbcConnectionDetails() { this.contextRunner .withUserConfiguration(EmbeddedDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class, MockFlywayMigrationStrategy.class) .withPropertyValues("spring.flyway.url=jdbc:hsqldb:mem:flywaytest", "spring.flyway.user=some-user", "spring.flyway.password=some-password", "spring.flyway.driver-class-name=org.hsqldb.jdbc.JDBCDriver") + .run((context) -> { + assertThat(context).hasSingleBean(Flyway.class); + Flyway flyway = context.getBean(Flyway.class); + DataSource dataSource = flyway.getConfiguration().getDataSource(); + assertThat(dataSource).isInstanceOf(SimpleDriverDataSource.class); + SimpleDriverDataSource simpleDriverDataSource = (SimpleDriverDataSource) dataSource; + assertThat(simpleDriverDataSource.getUrl()).isEqualTo("jdbc:hsqldb:mem:flywaytest"); + assertThat(simpleDriverDataSource.getUsername()).isEqualTo("some-user"); + assertThat(simpleDriverDataSource.getPassword()).isEqualTo("some-password"); + assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(org.hsqldb.jdbc.JDBCDriver.class); + }); + } + + @Test + void flywayConnectionDetailsAreUsedOverFlywayProperties() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, FlywayConnectionDetailsConfiguration.class, + MockFlywayMigrationStrategy.class) + .withPropertyValues("spring.flyway.url=jdbc:hsqldb:mem:flywaytest", "spring.flyway.user=some-user", + "spring.flyway.password=some-password", + "spring.flyway.driver-class-name=org.hsqldb.jdbc.JDBCDriver") .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); @@ -166,7 +188,7 @@ class FlywayAutoConfigurationTests { .isEqualTo("jdbc:postgresql://database.example.com:12345/database-1"); assertThat(simpleDriverDataSource.getUsername()).isEqualTo("user-1"); assertThat(simpleDriverDataSource.getPassword()).isEqualTo("secret-1"); - assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(org.postgresql.Driver.class); + assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(Driver.class); }); } @@ -246,6 +268,19 @@ class FlywayAutoConfigurationTests { }); } + @Test + void flywayDataSourceIsUsedWhenFlywayConnectionDetailsIsAvailable() { + this.contextRunner + .withUserConfiguration(FlywayDataSourceConfiguration.class, EmbeddedDataSourceConfiguration.class, + FlywayConnectionDetailsConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(FlywayConnectionDetails.class); + assertThat(context).hasSingleBean(Flyway.class); + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()) + .isEqualTo(context.getBean("flywayDataSource")); + }); + } + @Test void flywayDataSourceWithoutDataSourceAutoConfiguration() { this.contextRunner.withUserConfiguration(FlywayDataSourceConfiguration.class).run((context) -> { @@ -1109,4 +1144,31 @@ class FlywayAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class FlywayConnectionDetailsConfiguration { + + @Bean + FlywayConnectionDetails flywayConnectionDetails() { + return new FlywayConnectionDetails() { + + @Override + public String getJdbcUrl() { + return "jdbc:postgresql://database.example.com:12345/database-1"; + } + + @Override + public String getUsername() { + return "user-1"; + } + + @Override + public String getPassword() { + return "secret-1"; + } + + }; + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfigurationTests.java index 5ce38d8377..436b019172 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/influx/InfluxDbAutoConfigurationTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import retrofit2.Retrofit; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.PropertiesInfluxDbConnectionDetails; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -54,9 +55,17 @@ class InfluxDbAutoConfigurationTests { } @Test - void shouldUseConnectionDetails() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.withPropertyValues("spring.influx.url=http://localhost") + .run((context) -> assertThat(context).hasSingleBean(PropertiesInfluxDbConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { this.contextRunner.withBean(InfluxDbConnectionDetails.class, this::influxDbConnectionDetails).run((context) -> { - assertThat(context).hasSingleBean(InfluxDB.class); + assertThat(context).hasSingleBean(InfluxDB.class) + .hasSingleBean(InfluxDbConnectionDetails.class) + .doesNotHaveBean(PropertiesInfluxDbConnectionDetails.class); InfluxDB influxDb = context.getBean(InfluxDB.class); assertThat(influxDb).hasFieldOrPropertyWithValue("hostName", "localhost"); }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java index 75329fca7d..75cf075367 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java @@ -249,13 +249,21 @@ class DataSourceAutoConfigurationTests { } @Test - void dbcp2UsesJdbcConnectionDetailsIfAvailable() { + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesJdbcConnectionDetails.class)); + } + + @Test + void dbcp2UsesCustomConnectionDetailsWhenDefined() { ApplicationContextRunner runner = new ApplicationContextRunner() .withPropertyValues("spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource", "spring.datasource.dbcp2.url=jdbc:broken", "spring.datasource.dbcp2.username=alice", "spring.datasource.dbcp2.password=secret") - .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)); - runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> { + .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) + .withBean(JdbcConnectionDetails.class, TestJdbcConnectionDetails::new); + runner.run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); DataSource dataSource = context.getBean(DataSource.class); assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(BasicDataSource.class)) .satisfies((dbcp2) -> { @@ -268,11 +276,14 @@ class DataSourceAutoConfigurationTests { } @Test - void genericUsesJdbcConnectionDetailsIfAvailable() { + void genericUsesCustomJdbcConnectionDetailsWhenAvailable() { ApplicationContextRunner runner = new ApplicationContextRunner() .withPropertyValues("spring.datasource.type=" + TestDataSource.class.getName()) - .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)); - runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> { + .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) + .withBean(JdbcConnectionDetails.class, TestJdbcConnectionDetails::new); + runner.run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); DataSource dataSource = context.getBean(DataSource.class); assertThat(dataSource).isInstanceOf(TestDataSource.class); TestDataSource source = (TestDataSource) dataSource; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java index 73e02d1cdc..653d59cb8f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java @@ -104,10 +104,12 @@ class HikariDataSourceConfigurationTests { } @Test - void usesConnectionDetailsIfAvailable() { - this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class) + void usesCustomConnectionDetailsWhenDefined() { + this.contextRunner.withBean(JdbcConnectionDetails.class, TestJdbcConnectionDetails::new) .withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret") .run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); DataSource dataSource = context.getBean(DataSource.class); assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(HikariDataSource.class)) .satisfies((hikari) -> { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java index 6b901ea4d9..2343745409 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java @@ -28,8 +28,6 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; @@ -114,10 +112,12 @@ class OracleUcpDataSourceConfigurationTests { } @Test - void usesJdbcConnectionDetailsIfAvailable() { - this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class) + void usesCustomJdbcConnectionDetailsWhenDefined() { + this.contextRunner.withBean(JdbcConnectionDetails.class, TestJdbcConnectionDetails::new) .withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret") .run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); DataSource dataSource = context.getBean(DataSource.class); assertThat(dataSource).isInstanceOf(PoolDataSourceImpl.class); PoolDataSourceImpl oracleUcp = (PoolDataSourceImpl) dataSource; @@ -131,14 +131,4 @@ class OracleUcpDataSourceConfigurationTests { }); } - @Configuration(proxyBeanMethods = false) - static class ConnectionDetailsConfiguration { - - @Bean - JdbcConnectionDetails jdbcConnectionDetails() { - return new TestJdbcConnectionDetails(); - } - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java index 710bfd99c6..9c27204f79 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java @@ -117,10 +117,12 @@ class TomcatDataSourceConfigurationTests { } @Test - void usesJdbcConnectionDetailsIfAvailable() { - this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class) + void usesCustomJdbcConnectionDetailsWhenDefined() { + this.contextRunner.withBean(JdbcConnectionDetails.class, TestJdbcConnectionDetails::new) .withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret") .run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); DataSource dataSource = context.getBean(DataSource.class); assertThat(dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class); org.apache.tomcat.jdbc.pool.DataSource tomcat = (org.apache.tomcat.jdbc.pool.DataSource) dataSource; @@ -133,16 +135,6 @@ class TomcatDataSourceConfigurationTests { }); } - @Configuration(proxyBeanMethods = false) - static class ConnectionDetailsConfiguration { - - @Bean - JdbcConnectionDetails jdbcConnectionDetails() { - return new TestJdbcConnectionDetails(); - } - - } - @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties @EnableMBeanExport diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfigurationTests.java index 4eef023c9b..323a84d188 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfigurationTests.java @@ -96,7 +96,14 @@ class XADataSourceAutoConfigurationTests { } @Test - void shouldUseConnectionDetailsIfAvailable() { + void definesPropertiesBasedConnectionDetailsByDefault() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(XADataSourceAutoConfiguration.class)) + .withUserConfiguration(FromProperties.class) + .run((context) -> assertThat(context).hasSingleBean(PropertiesJdbcConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { JdbcConnectionDetails connectionDetails = mock(JdbcConnectionDetails.class); given(connectionDetails.getUsername()).willReturn("user-1"); given(connectionDetails.getPassword()).willReturn("password-1"); @@ -108,6 +115,8 @@ class XADataSourceAutoConfigurationTests { .withUserConfiguration(FromProperties.class) .withBean(JdbcConnectionDetails.class, () -> connectionDetails) .run((context) -> { + assertThat(context).hasSingleBean(JdbcConnectionDetails.class) + .doesNotHaveBean(PropertiesJdbcConnectionDetails.class); MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class); PGXADataSource dataSource = (PGXADataSource) wrapper.getXaDataSource(); assertThat(dataSource).isNotNull(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java index 013f574aeb..0d561322fe 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java @@ -164,6 +164,11 @@ class KafkaAutoConfigurationTests { }); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesKafkaConnectionDetails.class)); + } + @Test void connectionDetailsAreAppliedToConsumer() { this.contextRunner @@ -172,6 +177,8 @@ class KafkaAutoConfigurationTests { "spring.kafka.consumer.security.protocol=SSL") .withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails) .run((context) -> { + assertThat(context).hasSingleBean(KafkaConnectionDetails.class) + .doesNotHaveBean(PropertiesKafkaConnectionDetails.class); DefaultKafkaConsumerFactory consumerFactory = context.getBean(DefaultKafkaConsumerFactory.class); Map configs = consumerFactory.getConfigurationProperties(); assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, @@ -240,6 +247,8 @@ class KafkaAutoConfigurationTests { "spring.kafka.producer.security.protocol=SSL") .withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails) .run((context) -> { + assertThat(context).hasSingleBean(KafkaConnectionDetails.class) + .doesNotHaveBean(PropertiesKafkaConnectionDetails.class); DefaultKafkaProducerFactory producerFactory = context.getBean(DefaultKafkaProducerFactory.class); Map configs = producerFactory.getConfigurationProperties(); assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, @@ -298,6 +307,8 @@ class KafkaAutoConfigurationTests { "spring.kafka.admin.security.protocol=SSL") .withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails) .run((context) -> { + assertThat(context).hasSingleBean(KafkaConnectionDetails.class) + .doesNotHaveBean(PropertiesKafkaConnectionDetails.class); KafkaAdmin admin = context.getBean(KafkaAdmin.class); Map configs = admin.getConfigurationProperties(); assertThat(configs).containsEntry(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, @@ -363,6 +374,8 @@ class KafkaAutoConfigurationTests { "spring.kafka.security.protocol=SSL", "spring.kafka.streams.security.protocol=SSL") .withBean(KafkaConnectionDetails.class, this::kafkaConnectionDetails) .run((context) -> { + assertThat(context).hasSingleBean(KafkaConnectionDetails.class) + .doesNotHaveBean(PropertiesKafkaConnectionDetails.class); Properties configs = context .getBean(KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME, KafkaStreamsConfiguration.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java index 6b65518c75..cd62e79b75 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java @@ -156,6 +156,49 @@ class LiquibaseAutoConfigurationTests { })); } + @Test + void liquibaseDataSourceIsUsedOverLiquibaseConnectionDetails() { + this.contextRunner + .withUserConfiguration(LiquibaseDataSourceConfiguration.class, + LiquibaseConnectionDetailsConfiguration.class) + .run(assertLiquibase((liquibase) -> { + HikariDataSource dataSource = (HikariDataSource) liquibase.getDataSource(); + assertThat(dataSource.getJdbcUrl()).startsWith("jdbc:hsqldb:mem:liquibasetest"); + assertThat(dataSource.getUsername()).isEqualTo("sa"); + assertThat(dataSource.getPassword()).isNull(); + })); + } + + @Test + void liquibasePropertiesAreUsedOverJdbcConnectionDetails() { + this.contextRunner + .withPropertyValues("spring.liquibase.url=jdbc:hsqldb:mem:liquibasetest", "spring.liquibase.user=some-user", + "spring.liquibase.password=some-password", + "spring.liquibase.driver-class-name=org.hsqldb.jdbc.JDBCDriver") + .withUserConfiguration(JdbcConnectionDetailsConfiguration.class) + .run(assertLiquibase((liquibase) -> { + SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource(); + assertThat(dataSource.getUrl()).startsWith("jdbc:hsqldb:mem:liquibasetest"); + assertThat(dataSource.getUsername()).isEqualTo("some-user"); + assertThat(dataSource.getPassword()).isEqualTo("some-password"); + })); + } + + @Test + void liquibaseConnectionDetailsAreUsedOverLiquibaseProperties() { + this.contextRunner.withSystemProperties("shouldRun=false") + .withPropertyValues("spring.liquibase.url=jdbc:hsqldb:mem:liquibasetest", "spring.liquibase.user=some-user", + "spring.liquibase.password=some-password", + "spring.liquibase.driver-class-name=org.hsqldb.jdbc.JDBCDriver") + .withUserConfiguration(LiquibaseConnectionDetailsConfiguration.class) + .run(assertLiquibase((liquibase) -> { + SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource(); + assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://database.example.com:12345/database-1"); + assertThat(dataSource.getUsername()).isEqualTo("user-1"); + assertThat(dataSource.getPassword()).isEqualTo("secret-1"); + })); + } + @Test void changelogXml() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) @@ -578,6 +621,33 @@ class LiquibaseAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class LiquibaseConnectionDetailsConfiguration { + + @Bean + LiquibaseConnectionDetails liquibaseConnectionDetails() { + return new LiquibaseConnectionDetails() { + + @Override + public String getJdbcUrl() { + return "jdbc:postgresql://database.example.com:12345/database-1"; + } + + @Override + public String getUsername() { + return "user-1"; + } + + @Override + public String getPassword() { + return "secret-1"; + } + + }; + } + + } + static class CustomH2Driver extends org.h2.Driver { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java index abe1ec7a09..2f822731be 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.mongo; import java.util.concurrent.TimeUnit; +import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; @@ -84,6 +85,25 @@ class MongoAutoConfigurationTests { .run((context) -> assertThat(getSettings(context).getApplicationName()).isEqualTo("overridden-name")); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesMongoConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { + this.contextRunner.withBean(MongoConnectionDetails.class, () -> new MongoConnectionDetails() { + + @Override + public ConnectionString getConnectionString() { + return new ConnectionString("mongodb://localhost"); + } + + }) + .run((context) -> assertThat(context).hasSingleBean(MongoConnectionDetails.class) + .doesNotHaveBean(PropertiesMongoConnectionDetails.class)); + } + private MongoClientSettings getSettings(AssertableApplicationContext context) { assertThat(context).hasSingleBean(MongoClient.class); MongoClientImpl client = (MongoClientImpl) context.getBean(MongoClient.class); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java index 268c8963a1..d7163e31c1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.mongo; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; import com.mongodb.ReadPreference; import com.mongodb.connection.AsynchronousSocketChannelStreamFactoryFactory; @@ -111,6 +112,25 @@ class MongoReactiveAutoConfigurationTests { }); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesMongoConnectionDetails.class)); + } + + @Test + void shouldUseCustomConnectionDetailsWhenDefined() { + this.contextRunner.withBean(MongoConnectionDetails.class, () -> new MongoConnectionDetails() { + + @Override + public ConnectionString getConnectionString() { + return new ConnectionString("mongodb://localhost"); + } + + }) + .run((context) -> assertThat(context).hasSingleBean(MongoConnectionDetails.class) + .doesNotHaveBean(PropertiesMongoConnectionDetails.class)); + } + private MongoClientSettings getSettings(ApplicationContext context) { MongoClientImpl client = (MongoClientImpl) context.getBean(MongoClient.class); return client.getSettings(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java index d9b73ce74b..0bb4a6e250 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java @@ -105,8 +105,13 @@ class Neo4jAutoConfigurationTests { .hasMessageContaining("'%s' is not a supported scheme.", invalidScheme)); } + @Test + void definesPropertiesBasedConnectionDetailsByDefault() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(PropertiesNeo4jConnectionDetails.class)); + } + @Bean - void usesCustomConnectionDetails() { + void shouldUseCustomConnectionDetailsWhenDefined() { this.contextRunner.withBean(Neo4jConnectionDetails.class, () -> new Neo4jConnectionDetails() { @Override @@ -115,7 +120,9 @@ class Neo4jAutoConfigurationTests { } }).run((context) -> { - assertThat(context).hasSingleBean(Driver.class); + assertThat(context).hasSingleBean(Driver.class) + .hasSingleBean(Neo4jConnectionDetails.class) + .doesNotHaveBean(PropertiesNeo4jConnectionDetails.class); Driver driver = context.getBean(Driver.class); assertThat(driver.isEncrypted()).isTrue(); }); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/FlywayContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/FlywayContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..7345757531 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/FlywayContainerConnectionDetailsFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2023 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 org.springframework.boot.test.autoconfigure.flyway; + +import org.testcontainers.containers.JdbcDatabaseContainer; + +import org.springframework.boot.autoconfigure.flyway.FlywayConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer} + * fields that should produce {@link FlywayConnectionDetails}. + * + * @author Andy Wilkinson + */ +class FlywayContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + protected FlywayConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new FlywayContainerConnectionDetails(source); + } + + /** + * {@link FlywayConnectionDetails} backed by a {@link JdbcDatabaseContainer}. + */ + private static final class FlywayContainerConnectionDetails extends ContainerConnectionDetails + implements FlywayConnectionDetails { + + private final JdbcDatabaseContainer container; + + private FlywayContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public String getJdbcUrl() { + return this.container.getJdbcUrl(); + } + + @Override + public String getDriverClassName() { + return this.container.getDriverClassName(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/package-info.java new file mode 100644 index 0000000000..9640ee7bdf --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/flyway/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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. + */ + +/** + * Auto-configuration for using Flyway in tests. + */ +package org.springframework.boot.test.autoconfigure.flyway; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/LiquibaseContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/LiquibaseContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..1c23199ff2 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/LiquibaseContainerConnectionDetailsFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2023 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 org.springframework.boot.test.autoconfigure.liquibase; + +import org.testcontainers.containers.JdbcDatabaseContainer; + +import org.springframework.boot.autoconfigure.liquibase.LiquibaseConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer} + * fields that should produce {@link LiquibaseConnectionDetails}. + * + * @author Andy Wilkinson + */ +class LiquibaseContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + protected LiquibaseConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new LiquibaseContainerConnectionDetails(source); + } + + /** + * {@link LiquibaseConnectionDetails} backed by a {@link JdbcDatabaseContainer}. + */ + private static final class LiquibaseContainerConnectionDetails extends ContainerConnectionDetails + implements LiquibaseConnectionDetails { + + private final JdbcDatabaseContainer container; + + private LiquibaseContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public String getJdbcUrl() { + return this.container.getJdbcUrl(); + } + + @Override + public String getDriverClassName() { + return this.container.getDriverClassName(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/package-info.java new file mode 100644 index 0000000000..b27af084b8 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/liquibase/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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. + */ + +/** + * Auto-configuration for using Liquibase in tests. + */ +package org.springframework.boot.test.autoconfigure.liquibase; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index 3b4e87d7fb..bbae07cdbe 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -26,9 +26,11 @@ org.springframework.boot.test.autoconfigure.cassandra.CassandraContainerConnecti org.springframework.boot.test.autoconfigure.couchbase.CouchbaseContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.data.redis.RedisContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.flyway.FlywayContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.influx.InfluxDbContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.jdbc.JdbcContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.kafka.KafkaContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.mongo.MongoContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.neo4j.Neo4jContainerConnectionDetailsFactory,\ org.springframework.boot.test.autoconfigure.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\ diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java index 843044372f..2711fba1dc 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java @@ -23,9 +23,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayConnectionDetails; import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection; import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import static org.assertj.core.api.Assertions.assertThat; @@ -42,8 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; class CityRepositoryTests { @Container - @JdbcServiceConnection @R2dbcServiceConnection + @ServiceConnection(FlywayConnectionDetails.class) static PostgreSQLContainer postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) .withDatabaseName("test_flyway"); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java index 8a3497f184..af6ccd35e5 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java @@ -23,9 +23,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseConnectionDetails; import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; -import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection; import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import static org.assertj.core.api.Assertions.assertThat; @@ -42,8 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; class CityRepositoryTests { @Container - @JdbcServiceConnection @R2dbcServiceConnection + @ServiceConnection(LiquibaseConnectionDetails.class) static PostgreSQLContainer postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) .withDatabaseName("test_liquibase");