Make URL- and property-based pooling config mutually exclusive

Closes gh-28144
pull/28398/head
Andy Wilkinson 3 years ago
parent 54d0f76df8
commit f2b3f1f41f

@ -29,15 +29,21 @@ 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.SpringBootCondition;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties.Pool;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.r2dbc.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;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@ -51,39 +57,54 @@ abstract class ConnectionFactoryConfigurations {
protected static ConnectionFactory createConnectionFactory(R2dbcProperties properties, ClassLoader classLoader,
List<ConnectionFactoryOptionsBuilderCustomizer> optionsCustomizers) {
return org.springframework.boot.r2dbc.ConnectionFactoryBuilder
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties,
() -> EmbeddedDatabaseConnection.get(classLoader)))
.configure((options) -> {
for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers) {
optionsCustomizer.customize(options);
}
}).build();
try {
return org.springframework.boot.r2dbc.ConnectionFactoryBuilder
.withOptions(new ConnectionFactoryOptionsInitializer().initialize(properties,
() -> EmbeddedDatabaseConnection.get(classLoader)))
.configure((options) -> {
for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers) {
optionsCustomizer.customize(options);
}
}).build();
}
catch (IllegalStateException ex) {
String message = ex.getMessage();
if (message != null && message.contains("driver=pool")
&& !ClassUtils.isPresent("io.r2dbc.pool.ConnectionPool", classLoader)) {
throw new MissingR2dbcPoolDependencyException();
}
throw ex;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ConnectionPool.class)
@Conditional(PooledConnectionFactoryCondition.class)
@ConditionalOnMissingBean(ConnectionFactory.class)
static class Pool {
static class PoolConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ConnectionPool.class)
static class PooledConnectionFactoryConfiguration {
@Bean(destroyMethod = "dispose")
ConnectionPool connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
ConnectionFactory connectionFactory = createConnectionFactory(properties,
resourceLoader.getClassLoader(), customizers.orderedStream().collect(Collectors.toList()));
R2dbcProperties.Pool pool = properties.getPool();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
map.from(pool.getMaxIdleTime()).to(builder::maxIdleTime);
map.from(pool.getMaxLifeTime()).to(builder::maxLifeTime);
map.from(pool.getMaxAcquireTime()).to(builder::maxAcquireTime);
map.from(pool.getMaxCreateConnectionTime()).to(builder::maxCreateConnectionTime);
map.from(pool.getInitialSize()).to(builder::initialSize);
map.from(pool.getMaxSize()).to(builder::maxSize);
map.from(pool.getValidationQuery()).whenHasText().to(builder::validationQuery);
map.from(pool.getValidationDepth()).to(builder::validationDepth);
return new ConnectionPool(builder.build());
}
@Bean(destroyMethod = "dispose")
ConnectionPool connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
ObjectProvider<ConnectionFactoryOptionsBuilderCustomizer> customizers) {
ConnectionFactory connectionFactory = createConnectionFactory(properties, resourceLoader.getClassLoader(),
customizers.orderedStream().collect(Collectors.toList()));
R2dbcProperties.Pool pool = properties.getPool();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
map.from(pool.getMaxIdleTime()).to(builder::maxIdleTime);
map.from(pool.getMaxLifeTime()).to(builder::maxLifeTime);
map.from(pool.getMaxAcquireTime()).to(builder::maxAcquireTime);
map.from(pool.getMaxCreateConnectionTime()).to(builder::maxCreateConnectionTime);
map.from(pool.getInitialSize()).to(builder::initialSize);
map.from(pool.getMaxSize()).to(builder::maxSize);
map.from(pool.getValidationQuery()).whenHasText().to(builder::validationQuery);
map.from(pool.getValidationDepth()).to(builder::validationDepth);
return new ConnectionPool(builder.build());
}
}
@ -92,7 +113,7 @@ abstract class ConnectionFactoryConfigurations {
@ConditionalOnProperty(prefix = "spring.r2dbc.pool", value = "enabled", havingValue = "false",
matchIfMissing = true)
@ConditionalOnMissingBean(ConnectionFactory.class)
static class Generic {
static class GenericConfiguration {
@Bean
ConnectionFactory connectionFactory(R2dbcProperties properties, ResourceLoader resourceLoader,
@ -105,26 +126,35 @@ abstract class ConnectionFactoryConfigurations {
/**
* {@link Condition} that checks that a {@link ConnectionPool} is requested. The
* condition matches if pooling was opt-in via configuration and the r2dbc url does
* not contain pooling-related options.
* condition matches if pooling was opt-in via configuration. If any of the
* spring.r2dbc.pool.* properties have been configured, an exception is thrown if the
* URL also contains pooling-related options or io.r2dbc.pool.ConnectionPool is not on
* the class path.
*/
static class PooledConnectionFactoryCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean poolEnabled = context.getEnvironment().getProperty("spring.r2dbc.pool.enabled", Boolean.class,
true);
if (poolEnabled) {
// Make sure the URL does not have pool options
String url = context.getEnvironment().getProperty("spring.r2dbc.url");
boolean pooledUrl = StringUtils.hasText(url) && url.contains(":pool:");
if (pooledUrl) {
return ConditionOutcome.noMatch("R2DBC Connection URL contains pooling-related options");
BindResult<Pool> pool = Binder.get(context.getEnvironment()).bind("spring.r2dbc.pool",
Bindable.of(Pool.class));
if (hasPoolUrl(context.getEnvironment())) {
if (pool.isBound()) {
throw new MultipleConnectionPoolConfigurationsException();
}
return ConditionOutcome
.match("Pooling is enabled and R2DBC Connection URL does not contain pooling-related options");
return ConditionOutcome.noMatch("URL-based pooling has been configured");
}
if (pool.isBound() && !ClassUtils.isPresent("io.r2dbc.pool.ConnectionPool", context.getClassLoader())) {
throw new MissingR2dbcPoolDependencyException();
}
if (pool.orElseGet(Pool::new).isEnabled()) {
return ConditionOutcome.match("Property-based pooling is enabled");
}
return ConditionOutcome.noMatch("Pooling is disabled");
return ConditionOutcome.noMatch("Property-based pooling is disabled");
}
private boolean hasPoolUrl(Environment environment) {
String url = environment.getProperty("spring.r2dbc.url");
return StringUtils.hasText(url) && url.contains(":pool:");
}
}

@ -0,0 +1,32 @@
/*
* Copyright 2012-2021 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;
/**
* Exception thrown when R2DBC connection pooling has been configured but the
* {@code io.r2dbc:r2dbc-pool} dependency is missing.
*
* @author Andy Wilkinson
*/
class MissingR2dbcPoolDependencyException extends RuntimeException {
MissingR2dbcPoolDependencyException() {
super("R2DBC connection pooling has been configured but the io.r2dbc.pool.ConnectionPool class is not "
+ "present.");
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2012-2021 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 org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.FailureAnalyzer;
/**
* {@link FailureAnalyzer} for {@link MissingR2dbcPoolDependencyException}.
*
* @author Andy Wilkinson
*/
class MissingR2dbcPoolDependencyFailureAnalyzer extends AbstractFailureAnalyzer<MissingR2dbcPoolDependencyException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, MissingR2dbcPoolDependencyException cause) {
return new FailureAnalysis(cause.getMessage(),
"Update your application's build to depend on io.r2dbc:r2dbc-pool or your application's configuration "
+ "to disable R2DBC connection pooling.",
cause);
}
}

@ -0,0 +1,32 @@
/*
* Copyright 2012-2021 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;
/**
* Exception thrown when R2DBC connection pooling has been configured both by the URL
* ({@code spring.r2dbc.url}) and the pool properties ({@code spring.r2dbc.pool.*}.
*
* @author Andy Wilkinson
*/
class MultipleConnectionPoolConfigurationsException extends RuntimeException {
MultipleConnectionPoolConfigurationsException() {
super("R2DBC connection pooling configuration should be provided by either the spring.r2dbc.pool.* "
+ "properties or the spring.r2dbc.url property but both have been used.");
}
}

@ -0,0 +1,39 @@
/*
* Copyright 2012-2021 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 org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.FailureAnalyzer;
/**
* {@link FailureAnalyzer} for {@link MultipleConnectionPoolConfigurationsException}.
*
* @author Andy Wilkinson
*/
class MultipleConnectionPoolConfigurationsFailureAnalzyer
extends AbstractFailureAnalyzer<MultipleConnectionPoolConfigurationsException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, MultipleConnectionPoolConfigurationsException cause) {
return new FailureAnalysis(cause.getMessage(),
"Update your configuration so that R2DBC connection pooling is configured using either the "
+ "spring.r2dbc.url property or the spring.r2dbc.pool.* properties",
cause);
}
}

@ -40,8 +40,8 @@ import org.springframework.context.annotation.Import;
@ConditionalOnResource(resources = "classpath:META-INF/services/io.r2dbc.spi.ConnectionFactoryProvider")
@AutoConfigureBefore({ DataSourceAutoConfiguration.class, SqlInitializationAutoConfiguration.class })
@EnableConfigurationProperties(R2dbcProperties.class)
@Import({ ConnectionFactoryConfigurations.Pool.class, ConnectionFactoryConfigurations.Generic.class,
ConnectionFactoryDependentConfiguration.class })
@Import({ ConnectionFactoryConfigurations.PoolConfiguration.class,
ConnectionFactoryConfigurations.GenericConfiguration.class, ConnectionFactoryDependentConfiguration.class })
public class R2dbcAutoConfiguration {
}

@ -178,6 +178,11 @@ public class R2dbcProperties {
*/
private ValidationDepth validationDepth = ValidationDepth.LOCAL;
/**
* Whether pooling is enabled. Requires r2dbc-pool.
*/
private boolean enabled = true;
public Duration getMaxIdleTime() {
return this.maxIdleTime;
}
@ -242,6 +247,14 @@ public class R2dbcProperties {
this.validationDepth = validationDepth;
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

@ -1688,11 +1688,6 @@
"name": "spring.quartz.scheduler-name",
"defaultValue": "quartzScheduler"
},
{
"name": "spring.r2dbc.pool.enabled",
"type": "java.lang.Boolean",
"description": "Whether pooling is enabled. Enabled automatically if \"r2dbc-pool\" is on the classpath."
},
{
"name": "spring.r2dbc.pool.validation-depth",
"defaultValue": "local"

@ -165,6 +165,8 @@ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyze
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jooq.NoDslContextBeanFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MissingR2dbcPoolDependencyFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MultipleConnectionPoolConfigurationsFailureAnalzyer,\
org.springframework.boot.autoconfigure.r2dbc.NoConnectionFactoryBeanFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

@ -0,0 +1,42 @@
/*
* Copyright 2012-2021 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 org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MissingR2dbcPoolDependencyFailureAnalyzer}
*
* @author Andy Wilkinson
*/
class MissingR2dbcPoolDependencyFailureAnalyzerTests {
private final MissingR2dbcPoolDependencyFailureAnalyzer failureAnalyzer = new MissingR2dbcPoolDependencyFailureAnalyzer();
@Test
void analyzeWhenDifferentFailureShouldReturnNull() {
assertThat(this.failureAnalyzer.analyze(new Exception())).isNull();
}
@Test
void analyzeWhenMissingR2dbcPoolDependencyShouldReturnAnalysis() {
assertThat(this.failureAnalyzer.analyze(new MissingR2dbcPoolDependencyException())).isNotNull();
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2012-2021 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 org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MultipleConnectionPoolConfigurationsFailureAnalzyer}
*
* @author Andy Wilkinson
*/
class MultipleConnectionPoolConfigurationsFailureAnalzyerTests {
private final MultipleConnectionPoolConfigurationsFailureAnalzyer failureAnalyzer = new MultipleConnectionPoolConfigurationsFailureAnalzyer();
@Test
void analyzeWhenDifferentFailureShouldReturnNull() {
assertThat(this.failureAnalyzer.analyze(new Exception())).isNull();
}
@Test
void analyzeWhenMultipleConnectionPoolConfigurationsShouldReturnAnalysis() {
assertThat(this.failureAnalyzer.analyze(new MultipleConnectionPoolConfigurationsException())).isNotNull();
}
}

@ -20,7 +20,6 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.time.Duration;
import java.util.UUID;
import java.util.function.Function;
import javax.sql.DataSource;
@ -99,14 +98,31 @@ class R2dbcAutoConfigurationTests {
}
@Test
void configureWithUrlPoolAndPoolPropertiesApplyUrlPoolOptions() {
void configureWithUrlPoolAndPoolPropertiesFails() {
this.contextRunner
.withPropertyValues("spring.r2dbc.url:r2dbc:pool:h2:mem:///" + randomDatabaseName() + "?maxSize=12",
"spring.r2dbc.pool.max-size=15")
.run((context) -> assertThat(context).getFailure().getRootCause()
.isInstanceOf(MultipleConnectionPoolConfigurationsException.class));
}
@Test
void configureWithUrlPoolAndPropertyBasedPoolingDisabledFails() {
this.contextRunner
.withPropertyValues("spring.r2dbc.url:r2dbc:pool:h2:mem:///" + randomDatabaseName() + "?maxSize=12",
"spring.r2dbc.pool.enabled=false")
.run((context) -> assertThat(context).getFailure().getRootCause()
.isInstanceOf(MultipleConnectionPoolConfigurationsException.class));
}
@Test
void configureWithUrlPoolAndNoPoolPropertiesCreatesPool() {
this.contextRunner
.withPropertyValues("spring.r2dbc.url:r2dbc:pool:h2:mem:///" + randomDatabaseName() + "?maxSize=12")
.run((context) -> {
assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class);
PoolMetrics poolMetrics = context.getBean(ConnectionPool.class).getMetrics().get();
assertThat(poolMetrics.getMaxAllocatedSize()).isEqualTo(12);
ConnectionPool connectionPool = context.getBean(ConnectionPool.class);
assertThat(connectionPool.getMetrics().get().getMaxAllocatedSize()).isEqualTo(12);
});
}
@ -132,27 +148,6 @@ class R2dbcAutoConfigurationTests {
});
}
@Test
void configureWithoutR2dbcPoolCreateGenericConnectionFactory() {
this.contextRunner.with(hideConnectionPool()).withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///"
+ randomDatabaseName() + "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE").run((context) -> {
assertThat(context).hasSingleBean(ConnectionFactory.class);
assertThat(context.getBean(ConnectionFactory.class))
.asInstanceOf(type(OptionsCapableConnectionFactory.class))
.extracting(Wrapped<ConnectionFactory>::unwrap)
.isExactlyInstanceOf(H2ConnectionFactory.class);
});
}
@Test
void configureWithoutR2dbcPoolAndPoolEnabledDoesNotCreateConnectionFactory() {
this.contextRunner.with(hideConnectionPool())
.withPropertyValues("spring.r2dbc.pool.enabled=true",
"spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName()
+ "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
.run((context) -> assertThat(context).doesNotHaveBean(ConnectionFactory.class));
}
@Test
void configureWithoutPoolInvokeOptionCustomizer() {
this.contextRunner
@ -297,10 +292,6 @@ class R2dbcAutoConfigurationTests {
return "testdb-" + UUID.randomUUID();
}
private Function<ApplicationContextRunner, ApplicationContextRunner> hideConnectionPool() {
return (runner) -> runner.withClassLoader(new FilteredClassLoader("io.r2dbc.pool"));
}
private static class DisableEmbeddedDatabaseClassLoader extends URLClassLoader {
DisableEmbeddedDatabaseClassLoader() {

@ -0,0 +1,87 @@
/*
* Copyright 2012-2021 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 java.util.UUID;
import io.r2dbc.h2.H2ConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Wrapped;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.InstanceOfAssertFactory;
import org.assertj.core.api.ObjectAssert;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.r2dbc.OptionsCapableConnectionFactory;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link R2dbcAutoConfiguration} without the {@code io.r2dbc:r2dbc-pool}
* dependency.
*
* @author Andy Wilkinson
*/
@ClassPathExclusions("r2dbc-pool-*.jar")
class R2dbcAutoConfigurationWithoutConnectionPoolTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class));
@Test
void configureWithoutR2dbcPoolCreateGenericConnectionFactory() {
this.contextRunner.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName()
+ "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE").run((context) -> {
assertThat(context).hasSingleBean(ConnectionFactory.class);
assertThat(context.getBean(ConnectionFactory.class))
.asInstanceOf(type(OptionsCapableConnectionFactory.class))
.extracting(Wrapped<ConnectionFactory>::unwrap)
.isExactlyInstanceOf(H2ConnectionFactory.class);
});
}
@Test
void configureWithoutR2dbcPoolAndPoolEnabledShouldFail() {
this.contextRunner
.withPropertyValues("spring.r2dbc.pool.enabled=true",
"spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName()
+ "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
.run((context) -> assertThat(context).getFailure().getRootCause()
.isInstanceOf(MissingR2dbcPoolDependencyException.class));
}
@Test
void configureWithoutR2dbcPoolAndPoolUrlShouldFail() {
this.contextRunner
.withPropertyValues("spring.r2dbc.url:r2dbc:pool:h2:mem:///" + randomDatabaseName()
+ "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
.run((context) -> assertThat(context).getFailure().getRootCause()
.isInstanceOf(MissingR2dbcPoolDependencyException.class));
}
private <T> InstanceOfAssertFactory<T, ObjectAssert<T>> type(Class<T> type) {
return InstanceOfAssertFactories.type(type);
}
private String randomDatabaseName() {
return "testdb-" + UUID.randomUUID();
}
}
Loading…
Cancel
Save