mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add ConnectionDetail support to R2DBC auto-configuration
Update R2DBC auto-configuration so that `R2dbcConnectionDetails` beans may be optionally used to provide connection details. See gh-34657 Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com> Co-Authored-By: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
parent
d09ac00824
commit
61e9fe8cd4
@ -237,6 +237,7 @@ dependencies {
|
|||||||
testImplementation("org.mockito:mockito-core")
|
testImplementation("org.mockito:mockito-core")
|
||||||
testImplementation("org.mockito:mockito-junit-jupiter")
|
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||||
testImplementation("org.postgresql:postgresql")
|
testImplementation("org.postgresql:postgresql")
|
||||||
|
testImplementation("org.postgresql:r2dbc-postgresql")
|
||||||
testImplementation("org.skyscreamer:jsonassert")
|
testImplementation("org.skyscreamer:jsonassert")
|
||||||
testImplementation("org.springframework:spring-test")
|
testImplementation("org.springframework:spring-test")
|
||||||
testImplementation("org.springframework:spring-core-test")
|
testImplementation("org.springframework:spring-core-test")
|
||||||
|
@ -53,7 +53,7 @@ class ConnectionFactoryBeanCreationFailureAnalyzer
|
|||||||
private String getDescription(ConnectionFactoryBeanCreationException cause) {
|
private String getDescription(ConnectionFactoryBeanCreationException cause) {
|
||||||
StringBuilder description = new StringBuilder();
|
StringBuilder description = new StringBuilder();
|
||||||
description.append("Failed to configure a ConnectionFactory: ");
|
description.append("Failed to configure a ConnectionFactory: ");
|
||||||
if (!StringUtils.hasText(cause.getProperties().getUrl())) {
|
if (!StringUtils.hasText(cause.getUrl())) {
|
||||||
description.append("'url' attribute is not specified and ");
|
description.append("'url' attribute is not specified and ");
|
||||||
}
|
}
|
||||||
description.append(String.format("no embedded database could be configured.%n"));
|
description.append(String.format("no embedded database could be configured.%n"));
|
||||||
|
@ -51,14 +51,18 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Rodolpho S. Couto
|
* @author Rodolpho S. Couto
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
abstract class ConnectionFactoryConfigurations {
|
abstract class ConnectionFactoryConfigurations {
|
||||||
|
|
||||||
protected static ConnectionFactory createConnectionFactory(R2dbcProperties properties, ClassLoader classLoader,
|
protected static ConnectionFactory createConnectionFactory(R2dbcProperties properties,
|
||||||
|
R2dbcConnectionDetails connectionDetails, ClassLoader classLoader,
|
||||||
List<ConnectionFactoryOptionsBuilderCustomizer> optionsCustomizers) {
|
List<ConnectionFactoryOptionsBuilderCustomizer> optionsCustomizers) {
|
||||||
try {
|
try {
|
||||||
return org.springframework.boot.r2dbc.ConnectionFactoryBuilder
|
return org.springframework.boot.r2dbc.ConnectionFactoryBuilder
|
||||||
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties,
|
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties, connectionDetails,
|
||||||
() -> EmbeddedDatabaseConnection.get(classLoader)))
|
() -> EmbeddedDatabaseConnection.get(classLoader)))
|
||||||
.configure((options) -> {
|
.configure((options) -> {
|
||||||
for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers) {
|
for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers) {
|
||||||
@ -87,10 +91,12 @@ abstract class ConnectionFactoryConfigurations {
|
|||||||
static class PooledConnectionFactoryConfiguration {
|
static class PooledConnectionFactoryConfiguration {
|
||||||
|
|
||||||
@Bean(destroyMethod = "dispose")
|
@Bean(destroyMethod = "dispose")
|
||||||
ConnectionPool connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
|
ConnectionPool connectionFactory(R2dbcProperties properties,
|
||||||
|
ObjectProvider<R2dbcConnectionDetails> connectionDetails, ResourceLoader resourceLoader,
|
||||||
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
||||||
ConnectionFactory connectionFactory = createConnectionFactory(properties,
|
ConnectionFactory connectionFactory = createConnectionFactory(properties,
|
||||||
resourceLoader.getClassLoader(), customizers.orderedStream().toList());
|
connectionDetails.getIfAvailable(), resourceLoader.getClassLoader(),
|
||||||
|
customizers.orderedStream().toList());
|
||||||
R2dbcProperties.Pool pool = properties.getPool();
|
R2dbcProperties.Pool pool = properties.getPool();
|
||||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||||
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
|
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
|
||||||
@ -116,10 +122,11 @@ abstract class ConnectionFactoryConfigurations {
|
|||||||
static class GenericConfiguration {
|
static class GenericConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ConnectionFactory connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
|
ConnectionFactory connectionFactory(R2dbcProperties properties,
|
||||||
|
ObjectProvider<R2dbcConnectionDetails> connectionDetails, ResourceLoader resourceLoader,
|
||||||
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
|
||||||
return createConnectionFactory(properties, resourceLoader.getClassLoader(),
|
return createConnectionFactory(properties, connectionDetails.getIfAvailable(),
|
||||||
customizers.orderedStream().toList());
|
resourceLoader.getClassLoader(), customizers.orderedStream().toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,12 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.boot.autoconfigure.r2dbc;
|
package org.springframework.boot.autoconfigure.r2dbc;
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
||||||
import io.r2dbc.spi.Option;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.boot.r2dbc.EmbeddedDatabaseConnection;
|
import org.springframework.boot.r2dbc.EmbeddedDatabaseConnection;
|
||||||
@ -31,6 +29,9 @@ import org.springframework.util.StringUtils;
|
|||||||
* Initialize a {@link Builder} based on {@link R2dbcProperties}.
|
* Initialize a {@link Builder} based on {@link R2dbcProperties}.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
class ConnectionFactoryOptionsInitializer {
|
class ConnectionFactoryOptionsInitializer {
|
||||||
|
|
||||||
@ -38,45 +39,31 @@ class ConnectionFactoryOptionsInitializer {
|
|||||||
* Initialize a {@link Builder ConnectionFactoryOptions.Builder} using the specified
|
* Initialize a {@link Builder ConnectionFactoryOptions.Builder} using the specified
|
||||||
* properties.
|
* properties.
|
||||||
* @param properties the properties to use to initialize the builder
|
* @param properties the properties to use to initialize the builder
|
||||||
|
* @param connectionDetails the connection details to use to initialize the builder
|
||||||
* @param embeddedDatabaseConnection the embedded connection to use as a fallback
|
* @param embeddedDatabaseConnection the embedded connection to use as a fallback
|
||||||
* @return an initialized builder
|
* @return an initialized builder
|
||||||
* @throws ConnectionFactoryBeanCreationException if no suitable connection could be
|
* @throws ConnectionFactoryBeanCreationException if no suitable connection could be
|
||||||
* determined
|
* determined
|
||||||
*/
|
*/
|
||||||
ConnectionFactoryOptions.Builder initialize(R2dbcProperties properties,
|
ConnectionFactoryOptions.Builder initialize(R2dbcProperties properties, R2dbcConnectionDetails connectionDetails,
|
||||||
Supplier<EmbeddedDatabaseConnection> embeddedDatabaseConnection) {
|
Supplier<EmbeddedDatabaseConnection> embeddedDatabaseConnection) {
|
||||||
if (StringUtils.hasText(properties.getUrl())) {
|
if (connectionDetails != null) {
|
||||||
return initializeRegularOptions(properties);
|
return connectionDetails.getConnectionFactoryOptions().mutate();
|
||||||
}
|
}
|
||||||
EmbeddedDatabaseConnection embeddedConnection = embeddedDatabaseConnection.get();
|
EmbeddedDatabaseConnection embeddedConnection = embeddedDatabaseConnection.get();
|
||||||
if (embeddedConnection != EmbeddedDatabaseConnection.NONE) {
|
if (embeddedConnection != EmbeddedDatabaseConnection.NONE) {
|
||||||
return initializeEmbeddedOptions(properties, embeddedConnection);
|
return initializeEmbeddedOptions(properties, embeddedConnection);
|
||||||
}
|
}
|
||||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", properties,
|
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", null,
|
||||||
embeddedConnection);
|
embeddedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionFactoryOptions.Builder initializeRegularOptions(R2dbcProperties properties) {
|
|
||||||
ConnectionFactoryOptions urlOptions = ConnectionFactoryOptions.parse(properties.getUrl());
|
|
||||||
Builder optionsBuilder = urlOptions.mutate();
|
|
||||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.USER, properties::getUsername,
|
|
||||||
StringUtils::hasText);
|
|
||||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.PASSWORD, properties::getPassword,
|
|
||||||
StringUtils::hasText);
|
|
||||||
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.DATABASE,
|
|
||||||
() -> determineDatabaseName(properties), StringUtils::hasText);
|
|
||||||
if (properties.getProperties() != null) {
|
|
||||||
properties.getProperties().forEach((key, value) -> optionsBuilder.option(Option.valueOf(key), value));
|
|
||||||
}
|
|
||||||
return optionsBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder initializeEmbeddedOptions(R2dbcProperties properties,
|
private Builder initializeEmbeddedOptions(R2dbcProperties properties,
|
||||||
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||||
String url = embeddedDatabaseConnection.getUrl(determineEmbeddedDatabaseName(properties));
|
String url = embeddedDatabaseConnection.getUrl(determineEmbeddedDatabaseName(properties));
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL",
|
throw connectionFactoryBeanCreationException("Failed to determine a suitable R2DBC Connection URL", url,
|
||||||
properties, embeddedDatabaseConnection);
|
embeddedDatabaseConnection);
|
||||||
}
|
}
|
||||||
Builder builder = ConnectionFactoryOptions.parse(url).mutate();
|
Builder builder = ConnectionFactoryOptions.parse(url).mutate();
|
||||||
String username = determineEmbeddedUsername(properties);
|
String username = determineEmbeddedUsername(properties);
|
||||||
@ -89,6 +76,11 @@ class ConnectionFactoryOptionsInitializer {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String determineEmbeddedDatabaseName(R2dbcProperties properties) {
|
||||||
|
String databaseName = determineDatabaseName(properties);
|
||||||
|
return (databaseName != null) ? databaseName : "testdb";
|
||||||
|
}
|
||||||
|
|
||||||
private String determineDatabaseName(R2dbcProperties properties) {
|
private String determineDatabaseName(R2dbcProperties properties) {
|
||||||
if (properties.isGenerateUniqueName()) {
|
if (properties.isGenerateUniqueName()) {
|
||||||
return properties.determineUniqueName();
|
return properties.determineUniqueName();
|
||||||
@ -99,30 +91,14 @@ class ConnectionFactoryOptionsInitializer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String determineEmbeddedDatabaseName(R2dbcProperties properties) {
|
|
||||||
String databaseName = determineDatabaseName(properties);
|
|
||||||
return (databaseName != null) ? databaseName : "testdb";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String determineEmbeddedUsername(R2dbcProperties properties) {
|
private String determineEmbeddedUsername(R2dbcProperties properties) {
|
||||||
String username = ifHasText(properties.getUsername());
|
String username = ifHasText(properties.getUsername());
|
||||||
return (username != null) ? username : "sa";
|
return (username != null) ? username : "sa";
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends CharSequence> void configureIf(Builder optionsBuilder, ConnectionFactoryOptions originalOptions,
|
|
||||||
Option<T> option, Supplier<T> valueSupplier, Predicate<T> setIf) {
|
|
||||||
if (originalOptions.hasOption(option)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
T value = valueSupplier.get();
|
|
||||||
if (setIf.test(value)) {
|
|
||||||
optionsBuilder.option(option, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConnectionFactoryBeanCreationException connectionFactoryBeanCreationException(String message,
|
private ConnectionFactoryBeanCreationException connectionFactoryBeanCreationException(String message,
|
||||||
R2dbcProperties properties, EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
String r2dbcUrl, EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||||
return new ConnectionFactoryBeanCreationException(message, properties, embeddedDatabaseConnection);
|
return new ConnectionFactoryBeanCreationException(message, r2dbcUrl, embeddedDatabaseConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String ifHasText(String candidate) {
|
private String ifHasText(String candidate) {
|
||||||
@ -131,25 +107,25 @@ class ConnectionFactoryOptionsInitializer {
|
|||||||
|
|
||||||
static class ConnectionFactoryBeanCreationException extends BeanCreationException {
|
static class ConnectionFactoryBeanCreationException extends BeanCreationException {
|
||||||
|
|
||||||
private final R2dbcProperties properties;
|
private final String url;
|
||||||
|
|
||||||
private final EmbeddedDatabaseConnection embeddedDatabaseConnection;
|
private final EmbeddedDatabaseConnection embeddedDatabaseConnection;
|
||||||
|
|
||||||
ConnectionFactoryBeanCreationException(String message, R2dbcProperties properties,
|
ConnectionFactoryBeanCreationException(String message, String url,
|
||||||
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
EmbeddedDatabaseConnection embeddedDatabaseConnection) {
|
||||||
super(message);
|
super(message);
|
||||||
this.properties = properties;
|
this.url = url;
|
||||||
this.embeddedDatabaseConnection = embeddedDatabaseConnection;
|
this.embeddedDatabaseConnection = embeddedDatabaseConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
EmbeddedDatabaseConnection getEmbeddedDatabaseConnection() {
|
EmbeddedDatabaseConnection getEmbeddedDatabaseConnection() {
|
||||||
return this.embeddedDatabaseConnection;
|
return this.embeddedDatabaseConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
R2dbcProperties getProperties() {
|
|
||||||
return this.properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,16 +16,26 @@
|
|||||||
|
|
||||||
package org.springframework.boot.autoconfigure.r2dbc;
|
package org.springframework.boot.autoconfigure.r2dbc;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
|
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
||||||
|
import io.r2dbc.spi.Option;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
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.condition.ConditionalOnResource;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration;
|
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link EnableAutoConfiguration Auto-configuration} for R2DBC.
|
* {@link EnableAutoConfiguration Auto-configuration} for R2DBC.
|
||||||
@ -42,4 +52,63 @@ import org.springframework.context.annotation.Import;
|
|||||||
ConnectionFactoryConfigurations.GenericConfiguration.class, ConnectionFactoryDependentConfiguration.class })
|
ConnectionFactoryConfigurations.GenericConfiguration.class, ConnectionFactoryDependentConfiguration.class })
|
||||||
public class R2dbcAutoConfiguration {
|
public class R2dbcAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(R2dbcConnectionDetails.class)
|
||||||
|
@ConditionalOnProperty("spring.r2dbc.url")
|
||||||
|
PropertiesR2dbcConnectionDetails propertiesR2dbcConnectionDetails(R2dbcProperties properties) {
|
||||||
|
return new PropertiesR2dbcConnectionDetails(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts {@link R2dbcProperties} to {@link R2dbcConnectionDetails}.
|
||||||
|
*/
|
||||||
|
static class PropertiesR2dbcConnectionDetails implements R2dbcConnectionDetails {
|
||||||
|
|
||||||
|
private final R2dbcProperties properties;
|
||||||
|
|
||||||
|
PropertiesR2dbcConnectionDetails(R2dbcProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionFactoryOptions getConnectionFactoryOptions() {
|
||||||
|
ConnectionFactoryOptions urlOptions = ConnectionFactoryOptions.parse(this.properties.getUrl());
|
||||||
|
Builder optionsBuilder = urlOptions.mutate();
|
||||||
|
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.USER, this.properties::getUsername,
|
||||||
|
StringUtils::hasText);
|
||||||
|
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.PASSWORD, this.properties::getPassword,
|
||||||
|
StringUtils::hasText);
|
||||||
|
configureIf(optionsBuilder, urlOptions, ConnectionFactoryOptions.DATABASE,
|
||||||
|
() -> determineDatabaseName(this.properties), StringUtils::hasText);
|
||||||
|
if (this.properties.getProperties() != null) {
|
||||||
|
this.properties.getProperties()
|
||||||
|
.forEach((key, value) -> optionsBuilder.option(Option.valueOf(key), value));
|
||||||
|
}
|
||||||
|
return optionsBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends CharSequence> void configureIf(Builder optionsBuilder,
|
||||||
|
ConnectionFactoryOptions originalOptions, Option<T> option, Supplier<T> valueSupplier,
|
||||||
|
Predicate<T> setIf) {
|
||||||
|
if (originalOptions.hasOption(option)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
T value = valueSupplier.get();
|
||||||
|
if (setIf.test(value)) {
|
||||||
|
optionsBuilder.option(option, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineDatabaseName(R2dbcProperties properties) {
|
||||||
|
if (properties.isGenerateUniqueName()) {
|
||||||
|
return properties.determineUniqueName();
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(properties.getName())) {
|
||||||
|
return properties.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.r2dbc;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details required to establish a connection to an SQL service using R2DBC.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 3.1.0
|
||||||
|
*/
|
||||||
|
public interface R2dbcConnectionDetails extends ConnectionDetails {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection factory options for connecting to the database.
|
||||||
|
* @return the connection factory options
|
||||||
|
*/
|
||||||
|
ConnectionFactoryOptions getConnectionFactoryOptions();
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,7 @@ import io.r2dbc.h2.H2ConnectionFactory;
|
|||||||
import io.r2dbc.pool.ConnectionPool;
|
import io.r2dbc.pool.ConnectionPool;
|
||||||
import io.r2dbc.pool.PoolMetrics;
|
import io.r2dbc.pool.PoolMetrics;
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
import io.r2dbc.spi.ConnectionFactoryProvider;
|
import io.r2dbc.spi.ConnectionFactoryProvider;
|
||||||
import io.r2dbc.spi.Option;
|
import io.r2dbc.spi.Option;
|
||||||
import io.r2dbc.spi.Wrapped;
|
import io.r2dbc.spi.Wrapped;
|
||||||
@ -54,6 +55,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
class R2dbcAutoConfigurationTests {
|
class R2dbcAutoConfigurationTests {
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ class R2dbcAutoConfigurationTests {
|
|||||||
assertThat(context.getBean(ConnectionPool.class)).extracting(ConnectionPool::unwrap)
|
assertThat(context.getBean(ConnectionPool.class)).extracting(ConnectionPool::unwrap)
|
||||||
.satisfies((connectionFactory) -> assertThat(connectionFactory)
|
.satisfies((connectionFactory) -> assertThat(connectionFactory)
|
||||||
.asInstanceOf(type(OptionsCapableConnectionFactory.class))
|
.asInstanceOf(type(OptionsCapableConnectionFactory.class))
|
||||||
.extracting(Wrapped<ConnectionFactory>::unwrap)
|
.extracting(Wrapped::unwrap)
|
||||||
.isExactlyInstanceOf(H2ConnectionFactory.class));
|
.isExactlyInstanceOf(H2ConnectionFactory.class));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -306,6 +310,64 @@ class R2dbcAutoConfigurationTests {
|
|||||||
.doesNotHaveBean(DatabaseClient.class));
|
.doesNotHaveBean(DatabaseClient.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseCustomConnectionDetailsIfAvailable() {
|
||||||
|
this.contextRunner.withPropertyValues("spring.r2dbc.pool.enabled=false")
|
||||||
|
.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||||
|
.run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||||
|
OptionsCapableConnectionFactory connectionFactory = context
|
||||||
|
.getBean(OptionsCapableConnectionFactory.class);
|
||||||
|
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(12345);
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("database-1");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("user-1");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("password-1");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void configureWithUsernamePasswordAndUrlWithoutUserInfoUsesUsernameAndPassword() {
|
||||||
|
this.contextRunner
|
||||||
|
.withPropertyValues("spring.r2dbc.pool.enabled=false",
|
||||||
|
"spring.r2dbc.url:r2dbc:postgresql://postgres.example.com:4321/db", "spring.r2dbc.username=alice",
|
||||||
|
"spring.r2dbc.password=secret")
|
||||||
|
.run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||||
|
OptionsCapableConnectionFactory connectionFactory = context
|
||||||
|
.getBean(OptionsCapableConnectionFactory.class);
|
||||||
|
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(4321);
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("db");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("alice");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("secret");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void configureWithUsernamePasswordAndUrlWithUserInfoUsesUserInfo() {
|
||||||
|
this.contextRunner
|
||||||
|
.withPropertyValues("spring.r2dbc.pool.enabled=false",
|
||||||
|
"spring.r2dbc.url:r2dbc:postgresql://bob:password@postgres.example.com:9876/db",
|
||||||
|
"spring.r2dbc.username=alice", "spring.r2dbc.password=secret")
|
||||||
|
.run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(ConnectionFactory.class);
|
||||||
|
OptionsCapableConnectionFactory connectionFactory = context
|
||||||
|
.getBean(OptionsCapableConnectionFactory.class);
|
||||||
|
ConnectionFactoryOptions options = connectionFactory.getOptions();
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DRIVER)).isEqualTo("postgresql");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.HOST)).isEqualTo("postgres.example.com");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PORT)).isEqualTo(9876);
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.DATABASE)).isEqualTo("db");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.USER)).isEqualTo("bob");
|
||||||
|
assertThat(options.getValue(ConnectionFactoryOptions.PASSWORD)).isEqualTo("password");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private <T> InstanceOfAssertFactory<T, ObjectAssert<T>> type(Class<T> type) {
|
private <T> InstanceOfAssertFactory<T, ObjectAssert<T>> type(Class<T> type) {
|
||||||
return InstanceOfAssertFactories.type(type);
|
return InstanceOfAssertFactories.type(type);
|
||||||
}
|
}
|
||||||
@ -342,4 +404,22 @@ class R2dbcAutoConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ConnectionDetailsConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
R2dbcConnectionDetails r2dbcConnectionDetails() {
|
||||||
|
return new R2dbcConnectionDetails() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionFactoryOptions getConnectionFactoryOptions() {
|
||||||
|
return ConnectionFactoryOptions
|
||||||
|
.parse("r2dbc:postgresql://user-1:password-1@postgres.example.com:12345/database-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user