From 4b5e50ba940848a58982dfe1a4e4e1b9f47e8af9 Mon Sep 17 00:00:00 2001 From: Olga MaciaszekSharma Date: Fri, 29 Sep 2023 15:20:52 +0200 Subject: [PATCH 1/2] Instrument user-created DataSource for checkpoint-restore See gh-37630 --- .../jdbc/DataSourceAutoConfiguration.java | 3 +- ...aSourceCheckpointRestoreConfiguration.java | 57 +++++++++++++++++++ .../jdbc/DataSourceConfiguration.java | 8 --- .../HikariDataSourceConfigurationTests.java | 25 ++++++++ 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java 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 5a41f5d0bd..5ad8ff75ae 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 @@ -51,13 +51,14 @@ import org.springframework.util.StringUtils; * @author Phillip Webb * @author Stephane Nicoll * @author Kazuki Shimizu + * @author Olga Maciaszek-Sharma * @since 1.0.0 */ @AutoConfiguration(before = SqlInitializationAutoConfiguration.class) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") @EnableConfigurationProperties(DataSourceProperties.class) -@Import(DataSourcePoolMetadataProvidersConfiguration.class) +@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java new file mode 100644 index 0000000000..6156ea5915 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java @@ -0,0 +1,57 @@ +/* + * 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; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCheckpointRestore; +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.jdbc.HikariCheckpointRestoreLifecycle; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Checkpoint-restore specific configuration. + * + * @author Olga Maciaszek-Sharma + * @since 3.2.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnCheckpointRestore +@ConditionalOnBean(DataSource.class) +public class DataSourceCheckpointRestoreConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(HikariDataSource.class) + @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", + matchIfMissing = true) + static class Hikari { + + @Bean + @ConditionalOnMissingBean + HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource dataSource) { + return new HikariCheckpointRestoreLifecycle(dataSource); + } + + } + +} 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 dc6ec5d11b..1cebf37cd5 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,14 +25,12 @@ import oracle.jdbc.OracleConnection; import oracle.ucp.jdbc.PoolDataSourceImpl; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnCheckpointRestore; 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.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DatabaseDriver; -import org.springframework.boot.jdbc.HikariCheckpointRestoreLifecycle; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @@ -124,12 +122,6 @@ abstract class DataSourceConfiguration { return dataSource; } - @Bean - @ConditionalOnCheckpointRestore - HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource hikariDataSource) { - return new HikariCheckpointRestoreLifecycle(hikariDataSource); - } - } /** 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 617f276456..17f5183ae3 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 @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.HikariCheckpointRestoreLifecycle; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ClassPathOverrides; @@ -42,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Moritz Halbritter * @author Andy Wilkinson * @author Phillip Webb + * @author Olga Maciaszek-Sharma */ class HikariDataSourceConfigurationTests { @@ -148,6 +150,14 @@ class HikariDataSourceConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(HikariCheckpointRestoreLifecycle.class)); } + @Test + @ClassPathOverrides("org.crac:crac:1.3.0") + void whenCheckpointRestoreIsAvailableAndDataSourceInstantiatedByUserHikariAutoConfigRegistersLifecycleBean() { + this.contextRunner.withUserConfiguration(UserDataSourceConfiguration.class) + .withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName()) + .run((context) -> assertThat(context).hasSingleBean(HikariCheckpointRestoreLifecycle.class)); + } + @Configuration(proxyBeanMethods = false) static class ConnectionDetailsConfiguration { @@ -178,4 +188,19 @@ class HikariDataSourceConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class UserDataSourceConfiguration { + + @Bean + DataSource dataSource() { + return DataSourceBuilder.create() + .driverClassName("org.postgresql.Driver") + .url("jdbc:postgresql://localhost:5432/database") + .username("user") + .password("password") + .build(); + } + + } + } From 31008def76ecfc12376f69b491de0d0a0ff3079c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 10 Oct 2023 10:26:37 +0100 Subject: [PATCH 2/2] Polish "Instrument user-created DataSource for checkpoint-restore" See gh-37630 --- .../jdbc/DataSourceCheckpointRestoreConfiguration.java | 6 +----- .../jdbc/HikariDataSourceConfigurationTests.java | 6 ++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java index 6156ea5915..79d1a048b8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java @@ -24,7 +24,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnCheckpointRestore; 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.jdbc.HikariCheckpointRestoreLifecycle; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,17 +32,14 @@ import org.springframework.context.annotation.Configuration; * Checkpoint-restore specific configuration. * * @author Olga Maciaszek-Sharma - * @since 3.2.0 */ @Configuration(proxyBeanMethods = false) @ConditionalOnCheckpointRestore @ConditionalOnBean(DataSource.class) -public class DataSourceCheckpointRestoreConfiguration { +class DataSourceCheckpointRestoreConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(HikariDataSource.class) - @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", - matchIfMissing = true) static class Hikari { @Bean 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 17f5183ae3..3019be5437 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 @@ -140,21 +140,19 @@ class HikariDataSourceConfigurationTests { @ClassPathOverrides("org.crac:crac:1.3.0") void whenCheckpointRestoreIsAvailableAndDataSourceHasBeenWrappedHikariAutoConfigRegistersLifecycleBean() { this.contextRunner.withUserConfiguration(DataSourceWrapperConfiguration.class) - .withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName()) .run((context) -> assertThat(context).hasSingleBean(HikariCheckpointRestoreLifecycle.class)); } @Test void whenCheckpointRestoreIsNotAvailableHikariAutoConfigDoesNotRegisterLifecycleBean() { - this.contextRunner.withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName()) + this.contextRunner .run((context) -> assertThat(context).doesNotHaveBean(HikariCheckpointRestoreLifecycle.class)); } @Test @ClassPathOverrides("org.crac:crac:1.3.0") - void whenCheckpointRestoreIsAvailableAndDataSourceInstantiatedByUserHikariAutoConfigRegistersLifecycleBean() { + void whenCheckpointRestoreIsAvailableAndDataSourceIsFromUserConfigurationHikariAutoConfigRegistersLifecycleBean() { this.contextRunner.withUserConfiguration(UserDataSourceConfiguration.class) - .withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName()) .run((context) -> assertThat(context).hasSingleBean(HikariCheckpointRestoreLifecycle.class)); }