From 098a57affb40a4bb2f43724b0e69ff9948376716 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 2 Feb 2022 09:47:10 +0100 Subject: [PATCH] Upgrade to R2DBC Borca-RELEASE See gh-28524 --- .../r2dbc/R2dbcAutoConfigurationTests.java | 14 ++++--- .../spring-boot-dependencies/build.gradle | 2 +- .../spring-boot-docs/build.gradle | 2 +- .../boot/r2dbc/ConnectionFactoryBuilder.java | 42 ++++++++++++------- .../r2dbc/ConnectionFactoryBuilderTests.java | 31 +++++++++++--- .../build.gradle | 2 +- .../build.gradle | 2 +- 7 files changed, 65 insertions(+), 30 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java index 3c7d9ac491b..2f4f7d3093d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,7 +93,7 @@ class R2dbcAutoConfigurationTests { assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class) .hasSingleBean(R2dbcProperties.class); ConnectionPool connectionPool = context.getBean(ConnectionPool.class); - assertThat(connectionPool).hasFieldOrPropertyWithValue("maxAcquireTime", Duration.ZERO); + assertThat(connectionPool).hasFieldOrPropertyWithValue("maxAcquireTime", Duration.ofNanos(-1)); }); } @@ -156,8 +156,9 @@ class R2dbcAutoConfigurationTests { assertThat(context).hasSingleBean(ConnectionFactory.class).doesNotHaveBean(ConnectionPool.class); ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); assertThat(connectionFactory).asInstanceOf(type(OptionsCapableConnectionFactory.class)) - .extracting(OptionsCapableConnectionFactory::getOptions).satisfies((options) -> assertThat( - options.getRequiredValue(Option.valueOf("customized"))).isTrue()); + .extracting(OptionsCapableConnectionFactory::getOptions) + .satisfies((options) -> assertThat(options.getRequiredValue(Option.valueOf("customized"))) + .isEqualTo(Boolean.TRUE)); }); } @@ -169,8 +170,9 @@ class R2dbcAutoConfigurationTests { ConnectionFactory pool = context.getBean(ConnectionFactory.class); ConnectionFactory connectionFactory = ((ConnectionPool) pool).unwrap(); assertThat(connectionFactory).asInstanceOf(type(OptionsCapableConnectionFactory.class)) - .extracting(OptionsCapableConnectionFactory::getOptions).satisfies((options) -> assertThat( - options.getRequiredValue(Option.valueOf("customized"))).isTrue()); + .extracting(OptionsCapableConnectionFactory::getOptions) + .satisfies((options) -> assertThat(options.getRequiredValue(Option.valueOf("customized"))) + .isEqualTo(Boolean.TRUE)); }); } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 8499fd22c82..1c428f674c1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1389,7 +1389,7 @@ bom { ] } } - library("R2DBC Bom", "Arabba-SR12") { + library("R2DBC Bom", "Borca-RELEASE") { group("io.r2dbc") { imports = [ "r2dbc-bom" diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 2a23cb83014..e5691af8c58 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -80,7 +80,6 @@ dependencies { implementation("io.micrometer:micrometer-registry-graphite") implementation("io.micrometer:micrometer-registry-jmx") implementation("io.projectreactor.netty:reactor-netty-http") - implementation("io.r2dbc:r2dbc-postgresql") implementation("io.undertow:undertow-core") implementation("jakarta.annotation:jakarta.annotation-api") implementation("jakarta.jms:jakarta.jms-api") @@ -114,6 +113,7 @@ dependencies { } implementation("org.mockito:mockito-core") implementation("org.mongodb:mongodb-driver-sync") + implementation("org.postgresql:r2dbc-postgresql") implementation("org.quartz-scheduler:quartz") implementation("org.slf4j:jul-to-slf4j") implementation("org.springframework:spring-jdbc") diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilder.java index 1125d53d3b0..04d583391a8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilder.java @@ -24,11 +24,13 @@ import java.util.function.Function; import io.r2dbc.pool.ConnectionPool; import io.r2dbc.pool.ConnectionPoolConfiguration; import io.r2dbc.pool.PoolingConnectionFactoryProvider; +import io.r2dbc.spi.Connection; import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryOptions; import io.r2dbc.spi.ConnectionFactoryOptions.Builder; import io.r2dbc.spi.ValidationDepth; +import org.reactivestreams.Publisher; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.util.Assert; @@ -209,7 +211,7 @@ public final class ConnectionFactoryBuilder { } private ConnectionFactoryOptions delegateFactoryOptions(ConnectionFactoryOptions options) { - String protocol = options.getRequiredValue(ConnectionFactoryOptions.PROTOCOL); + String protocol = toString(options.getRequiredValue(ConnectionFactoryOptions.PROTOCOL)); if (protocol.trim().length() == 0) { throw new IllegalArgumentException(String.format("Protocol %s is not valid.", protocol)); } @@ -221,35 +223,45 @@ public final class ConnectionFactoryBuilder { .option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate).build(); } + @SuppressWarnings("unchecked") ConnectionPoolConfiguration connectionPoolConfiguration(ConnectionFactoryOptions options, ConnectionFactory connectionFactory) { ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.BACKGROUND_EVICTION_INTERVAL)) + map.from(options.getValue(PoolingConnectionFactoryProvider.BACKGROUND_EVICTION_INTERVAL)) .as(this::toDuration).to(builder::backgroundEvictionInterval); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.INITIAL_SIZE)).as(this::toInteger) + map.from(options.getValue(PoolingConnectionFactoryProvider.INITIAL_SIZE)).as(this::toInteger) .to(builder::initialSize); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.MAX_SIZE)).as(this::toInteger) + map.from(options.getValue(PoolingConnectionFactoryProvider.MAX_SIZE)).as(this::toInteger) .to(builder::maxSize); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.ACQUIRE_RETRY)).as(this::toInteger) + map.from(options.getValue(PoolingConnectionFactoryProvider.ACQUIRE_RETRY)).as(this::toInteger) .to(builder::acquireRetry); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.MAX_LIFE_TIME)).as(this::toDuration) + map.from(options.getValue(PoolingConnectionFactoryProvider.MAX_LIFE_TIME)).as(this::toDuration) .to(builder::maxLifeTime); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.MAX_ACQUIRE_TIME)).as(this::toDuration) + map.from(options.getValue(PoolingConnectionFactoryProvider.MAX_ACQUIRE_TIME)).as(this::toDuration) .to(builder::maxAcquireTime); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.MAX_IDLE_TIME)).as(this::toDuration) + map.from(options.getValue(PoolingConnectionFactoryProvider.MAX_IDLE_TIME)).as(this::toDuration) .to(builder::maxIdleTime); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.MAX_CREATE_CONNECTION_TIME)) - .as(this::toDuration).to(builder::maxCreateConnectionTime); - map.from(options.getValue(PoolingConnectionFactoryProvider.POOL_NAME)).to(builder::name); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.REGISTER_JMX)).as(this::toBoolean) + map.from(options.getValue(PoolingConnectionFactoryProvider.MAX_CREATE_CONNECTION_TIME)).as(this::toDuration) + .to(builder::maxCreateConnectionTime); + map.from(options.getValue(PoolingConnectionFactoryProvider.POOL_NAME)).as(this::toString).to(builder::name); + map.from(options.getValue(PoolingConnectionFactoryProvider.PRE_RELEASE)).to((function) -> builder + .preRelease((Function>) function)); + map.from(options.getValue(PoolingConnectionFactoryProvider.POST_ALLOCATE)).to((function) -> builder + .postAllocate((Function>) function)); + map.from(options.getValue(PoolingConnectionFactoryProvider.REGISTER_JMX)).as(this::toBoolean) .to(builder::registerJmx); - map.from(options.getValue(PoolingConnectionFactoryProvider.VALIDATION_QUERY)).to(builder::validationQuery); - map.from((Object) options.getValue(PoolingConnectionFactoryProvider.VALIDATION_DEPTH)) - .as(this::toValidationDepth).to(builder::validationDepth); + map.from(options.getValue(PoolingConnectionFactoryProvider.VALIDATION_QUERY)).as(this::toString) + .to(builder::validationQuery); + map.from(options.getValue(PoolingConnectionFactoryProvider.VALIDATION_DEPTH)).as(this::toValidationDepth) + .to(builder::validationDepth); return builder.build(); } + private String toString(Object object) { + return toType(String.class, object, String::valueOf); + } + private Integer toInteger(Object object) { return toType(Integer.class, object, Integer::valueOf); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilderTests.java index 443d9c80ea4..3b4896666da 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.function.Function; import io.r2dbc.h2.H2ConnectionFactoryMetadata; import io.r2dbc.pool.ConnectionPool; @@ -35,7 +36,9 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.boot.r2dbc.ConnectionFactoryBuilder.PoolingAwareOptionsCapableWrapper; +import org.springframework.core.ResolvableType; import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldFilter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -198,9 +201,13 @@ class ConnectionFactoryBuilderTests { assertThat(configuration).extracting(expectedOption.property).isEqualTo(expectedOption.value); } + private static Iterable poolingConnectionProviderOptions() { + return extractPoolingConnectionProviderOptions((field) -> Option.class.equals(field.getType())); + } + @ParameterizedTest @SuppressWarnings({ "rawtypes", "unchecked" }) - @MethodSource("poolingConnectionProviderOptions") + @MethodSource("primitivePoolingConnectionProviderOptions") void stringlyTypedOptionIsMappedWhenCreatingPoolConfiguration(Option option) { String url = "r2dbc:pool:h2:mem:///" + UUID.randomUUID(); ExpectedOption expectedOption = ExpectedOption.get(option); @@ -213,11 +220,21 @@ class ConnectionFactoryBuilderTests { assertThat(configuration).extracting(expectedOption.property).isEqualTo(expectedOption.value); } - private static Iterable poolingConnectionProviderOptions() { + private static Iterable primitivePoolingConnectionProviderOptions() { + return extractPoolingConnectionProviderOptions((field) -> { + ResolvableType type = ResolvableType.forField(field); + if (!type.toClass().equals(Option.class)) { + return false; + } + Class valueType = type.as(Option.class).getGenerics()[0].toClass(); + return valueType.getPackage().getName().equals("java.lang"); + }); + } + + private static Iterable extractPoolingConnectionProviderOptions(FieldFilter filter) { List arguments = new ArrayList<>(); ReflectionUtils.doWithFields(PoolingConnectionFactoryProvider.class, - (field) -> arguments.add(Arguments.of(ReflectionUtils.getField(field, null))), - (field) -> Option.class.equals(field.getType())); + (field) -> arguments.add(Arguments.of(ReflectionUtils.getField(field, null))), filter); return arguments; } @@ -250,6 +267,10 @@ class ConnectionFactoryBuilderTests { POOL_NAME(PoolingConnectionFactoryProvider.POOL_NAME, "testPool", "name"), + POST_ALLOCATE(PoolingConnectionFactoryProvider.POST_ALLOCATE, mock(Function.class), "postAllocate"), + + PRE_RELEASE(PoolingConnectionFactoryProvider.PRE_RELEASE, mock(Function.class), "preRelease"), + REGISTER_JMX(PoolingConnectionFactoryProvider.REGISTER_JMX, true, "registerJmx"), VALIDATION_QUERY(PoolingConnectionFactoryProvider.VALIDATION_QUERY, "SELECT 1", "validationQuery"), diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle index 3f4a53ce7c7..32ac145609a 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle @@ -8,9 +8,9 @@ description = "Spring Boot Data R2DBC with Flyway smoke test" dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-data-r2dbc")) - runtimeOnly("io.r2dbc:r2dbc-postgresql") runtimeOnly("org.flywaydb:flyway-core") runtimeOnly("org.postgresql:postgresql") + runtimeOnly("org.postgresql:r2dbc-postgresql") runtimeOnly("org.springframework:spring-jdbc") testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle index c37dac862e2..19fb9b574c5 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle @@ -8,12 +8,12 @@ description = "Spring Boot Data R2DBC with Liquibase smoke test" dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-data-r2dbc")) - runtimeOnly("io.r2dbc:r2dbc-postgresql") runtimeOnly("org.liquibase:liquibase-core") { exclude group: "javax.activation", module: "javax.activation-api" exclude group: "javax.xml.bind", module: "jaxb-api" } runtimeOnly("org.postgresql:postgresql") + runtimeOnly("org.postgresql:r2dbc-postgresql") runtimeOnly("org.springframework:spring-jdbc") testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))