From b6d31fb6a1bf7fcafc3fdcdfb30cb0bfcb9e296c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 26 May 2021 13:37:11 -0700 Subject: [PATCH] Use correct type for deriveFromProperties Fix `DataSourceBuilder` so that the type used to access `deriveFrom` properties is based on the actual instance type rather than the user-defined type which could have been changed. Fixes gh-26644 --- .../boot/jdbc/DataSourceBuilder.java | 31 ++++++++++--------- .../boot/jdbc/DataSourceBuilderTests.java | 13 ++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java index f9aa8af28d7..c5dbe320331 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java @@ -91,7 +91,7 @@ public final class DataSourceBuilder { private Class type; - private final T deriveFrom; + private final DataSource deriveFrom; private DataSourceBuilder(ClassLoader classLoader) { this.classLoader = classLoader; @@ -168,25 +168,18 @@ public final class DataSourceBuilder { */ public T build() { DataSourceProperties properties = DataSourceProperties.forType(this.classLoader, this.type); - DataSourceProperties deriveFromProperties = (this.deriveFrom != null) - ? DataSourceProperties.forType(this.classLoader, this.type) : null; + DataSourceProperties deriveFromProperties = getDeriveFromProperties(); Class instanceType = (this.type != null) ? this.type : properties.getDataSourceInstanceType(); T dataSource = BeanUtils.instantiateClass(instanceType); Set applied = new HashSet<>(); for (DataSourceProperty property : DataSourceProperty.values()) { - if (this.values.containsKey(property)) { - String value = this.values.get(property); - if (value != null) { - properties.set(dataSource, property, value); - applied.add(property); - } + String value = this.values.get(property); + if (!this.values.containsKey(property) && deriveFromProperties != null && properties.canSet(property)) { + value = deriveFromProperties.get(this.deriveFrom, property); } - else if (deriveFromProperties != null && properties.canSet(property)) { - String value = deriveFromProperties.get(this.deriveFrom, property); - if (value != null) { - properties.set(dataSource, property, value); - applied.add(property); - } + if (value != null) { + properties.set(dataSource, property, value); + applied.add(property); } } if (!applied.contains(DataSourceProperty.DRIVER_CLASS_NAME) @@ -199,6 +192,14 @@ public final class DataSourceBuilder { return dataSource; } + @SuppressWarnings("unchecked") + private DataSourceProperties getDeriveFromProperties() { + if (this.deriveFrom == null) { + return null; + } + return DataSourceProperties.forType(this.classLoader, (Class) this.deriveFrom.getClass()); + } + /** * Create a new {@link DataSourceBuilder} instance. * @return a new datasource builder instance diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/DataSourceBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/DataSourceBuilderTests.java index f8428a8ecf2..c0cbbe687d8 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/DataSourceBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/DataSourceBuilderTests.java @@ -318,6 +318,19 @@ class DataSourceBuilderTests { assertThat(built.getUrl()).startsWith("jdbc:hsqldb:mem"); } + @Test // gh-26644 + void buildWhenDerivedFromExistingDatabaseWithTypeChange() { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setUsername("test"); + dataSource.setPassword("secret"); + dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres"); + DataSourceBuilder builder = DataSourceBuilder.derivedFrom(dataSource).type(SimpleDriverDataSource.class); + SimpleDriverDataSource built = (SimpleDriverDataSource) builder.username("test2").password("secret2").build(); + assertThat(built.getUsername()).isEqualTo("test2"); + assertThat(built.getPassword()).isEqualTo("secret2"); + assertThat(built.getUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres"); + } + final class HidePackagesClassLoader extends URLClassLoader { private final String[] hiddenPackages;