From a21ab392b635b1c95b39d12d8c18d21bccbda4d8 Mon Sep 17 00:00:00 2001 From: Fabio Grassi Date: Thu, 17 Sep 2020 23:00:16 +0200 Subject: [PATCH] Add support for Oracle UCP See gh-23403 --- .../spring-boot-autoconfigure/build.gradle | 1 + .../jdbc/DataSourceAutoConfiguration.java | 5 +- .../jdbc/DataSourceConfiguration.java | 33 ++++++ ...rcePoolMetadataProvidersConfiguration.java | 20 ++++ .../jdbc/UcpDataSourceConfigurationTests.java | 105 ++++++++++++++++++ spring-boot-project/spring-boot/build.gradle | 3 +- .../boot/jdbc/DataSourceBuilder.java | 19 ++-- .../metadata/UcpDataSourcePoolMetadata.java | 81 ++++++++++++++ .../boot/jdbc/DataSourceBuilderTests.java | 26 ++++- .../UcpDataSourcePoolMetadataTests.java | 76 +++++++++++++ 10 files changed, 355 insertions(+), 14 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/UcpDataSourceConfigurationTests.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadata.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadataTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index decb94b99b..bcbe04aead 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -64,6 +64,7 @@ dependencies { optional("org.codehaus.groovy:groovy-templates") optional("com.github.ben-manes.caffeine:caffeine") optional("com.github.mxab.thymeleaf.extras:thymeleaf-extras-data-attribute") + optional("com.oracle.database.jdbc:ucp") optional("com.sendgrid:sendgrid-java") optional("com.unboundid:unboundid-ldapsdk") optional("com.zaxxer:HikariCP") 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 8e4f1fb0a8..53086b0ffb 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 @@ -48,6 +48,7 @@ import org.springframework.util.StringUtils; * @author Phillip Webb * @author Stephane Nicoll * @author Kazuki Shimizu + * @author Fabio Grassi * @since 1.0.0 */ @Configuration(proxyBeanMethods = false) @@ -69,8 +70,8 @@ public class DataSourceAutoConfiguration { @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, - DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, - DataSourceJmxConfiguration.class }) + DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Ucp.class, + DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { } 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 d94e9d3ec1..cbf96da469 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 @@ -16,9 +16,13 @@ package org.springframework.boot.autoconfigure.jdbc; +import java.sql.SQLException; + import javax.sql.DataSource; import com.zaxxer.hikari.HikariDataSource; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceImpl; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -27,6 +31,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.util.StringUtils; /** @@ -35,6 +40,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Phillip Webb * @author Stephane Nicoll + * @author Fabio Grassi */ abstract class DataSourceConfiguration { @@ -109,6 +115,33 @@ abstract class DataSourceConfiguration { } + /** + * UCP DataSource configuration. + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(PoolDataSource.class) + @ConditionalOnMissingBean(DataSource.class) + @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "oracle.ucp.jdbc.PoolDataSource", + matchIfMissing = true) + static class Ucp { + + @Bean + @ConfigurationProperties(prefix = "spring.datasource.ucp") + PoolDataSource dataSource(DataSourceProperties properties) { + PoolDataSource dataSource = createDataSource(properties, PoolDataSourceImpl.class); + if (StringUtils.hasText(properties.getName())) { + try { + dataSource.setConnectionPoolName(properties.getName()); + } + catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error setting property connectionPoolName", se); + } + } + return dataSource; + } + + } + /** * Generic DataSource configuration. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java index d34a183bfb..ca12b29c0e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.jdbc.metadata; import com.zaxxer.hikari.HikariDataSource; +import oracle.ucp.jdbc.PoolDataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -25,6 +26,7 @@ import org.springframework.boot.jdbc.metadata.CommonsDbcp2DataSourcePoolMetadata import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.boot.jdbc.metadata.HikariDataSourcePoolMetadata; import org.springframework.boot.jdbc.metadata.TomcatDataSourcePoolMetadata; +import org.springframework.boot.jdbc.metadata.UcpDataSourcePoolMetadata; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,6 +35,7 @@ import org.springframework.context.annotation.Configuration; * sources. * * @author Stephane Nicoll + * @author Fabio Grassi * @since 1.2.0 */ @Configuration(proxyBeanMethods = false) @@ -90,4 +93,21 @@ public class DataSourcePoolMetadataProvidersConfiguration { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(PoolDataSource.class) + static class UcpPoolDataSourceMetadataProviderConfiguration { + + @Bean + DataSourcePoolMetadataProvider UcpPoolDataSourceMetadataProvider() { + return (dataSource) -> { + PoolDataSource ucpDataSource = DataSourceUnwrapper.unwrap(dataSource, PoolDataSource.class); + if (ucpDataSource != null) { + return new UcpDataSourcePoolMetadata(ucpDataSource); + } + return null; + }; + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/UcpDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/UcpDataSourceConfigurationTests.java new file mode 100644 index 0000000000..a329908d7f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/UcpDataSourceConfigurationTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2012-2020 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 static org.assertj.core.api.Assertions.assertThat; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceImpl; + +/** + * Tests for {@link DataSourceAutoConfiguration} with Oracle UCP. + * + * @author Fabio Grassi + */ +class UcpDataSourceConfigurationTests { + + private static final String PREFIX = "spring.datasource.ucp."; + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) + .withPropertyValues("spring.datasource.initialization-mode=never", + "spring.datasource.type=" + PoolDataSource.class.getName()); + + @Test + void testDataSourceExists() { + this.contextRunner.run((context) -> { + assertThat(context.getBeansOfType(DataSource.class)).hasSize(1); + assertThat(context.getBeansOfType(PoolDataSourceImpl.class)).hasSize(1); + }); + } + + @Test + void testDataSourcePropertiesOverridden() { + this.contextRunner.withPropertyValues(PREFIX + "URL=jdbc:foo//bar/spam", PREFIX + "maxIdleTime=1234") + .run((context) -> { + PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class); + assertThat(ds.getURL()).isEqualTo("jdbc:foo//bar/spam"); + assertThat(ds.getMaxIdleTime()).isEqualTo(1234); + }); + } + + @Test + void testDataSourceConnectionPropertiesOverridden() { + this.contextRunner.withPropertyValues(PREFIX + "connectionProperties.autoCommit=false").run((context) -> { + PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class); + assertThat(ds.getConnectionProperty("autoCommit")).isEqualTo("false"); + + }); + } + + @Test + void testDataSourceDefaultsPreserved() { + this.contextRunner.run((context) -> { + PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class); + assertThat(ds.getInitialPoolSize()).isEqualTo(0); + assertThat(ds.getMinPoolSize()).isEqualTo(0); + assertThat(ds.getMaxPoolSize()).isEqualTo(Integer.MAX_VALUE); + assertThat(ds.getInactiveConnectionTimeout()).isEqualTo(0); + assertThat(ds.getConnectionWaitTimeout()).isEqualTo(3); + assertThat(ds.getTimeToLiveConnectionTimeout()).isEqualTo(0); + assertThat(ds.getAbandonedConnectionTimeout()).isEqualTo(0); + assertThat(ds.getTimeoutCheckInterval()).isEqualTo(30); + assertThat(ds.getFastConnectionFailoverEnabled()).isFalse(); + }); + } + + @Test + void nameIsAliasedToPoolName() { + this.contextRunner.withPropertyValues("spring.datasource.name=myDS").run((context) -> { + PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class); + assertThat(ds.getConnectionPoolName()).isEqualTo("myDS"); + + }); + } + + @Test + void connectionPoolNameTakesPrecedenceOverName() { + this.contextRunner.withPropertyValues("spring.datasource.name=myDS", PREFIX + "connectionPoolName=myUcpDS") + .run((context) -> { + PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class); + assertThat(ds.getConnectionPoolName()).isEqualTo("myUcpDS"); + }); + } + +} diff --git a/spring-boot-project/spring-boot/build.gradle b/spring-boot-project/spring-boot/build.gradle index 1db702c445..90d6bc9dbb 100644 --- a/spring-boot-project/spring-boot/build.gradle +++ b/spring-boot-project/spring-boot/build.gradle @@ -21,6 +21,7 @@ dependencies { optional("com.atomikos:transactions-jta") optional("com.fasterxml.jackson.core:jackson-databind") optional("com.google.code.gson:gson") + optional("com.oracle.database.jdbc:ucp") optional("com.samskivert:jmustache") optional("com.zaxxer:HikariCP") optional("io.netty:netty-tcnative-boringssl-static") @@ -77,7 +78,7 @@ dependencies { testImplementation("com.ibm.db2:jcc") testImplementation("com.jayway.jsonpath:json-path") testImplementation("com.microsoft.sqlserver:mssql-jdbc") - testImplementation("com.oracle.ojdbc:ojdbc8") + testImplementation("com.oracle.database.jdbc:ojdbc8") testImplementation("com.squareup.okhttp3:okhttp") testImplementation("com.sun.xml.messaging.saaj:saaj-impl") testImplementation("io.projectreactor:reactor-test") 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 9f5f2e698e..9bf1a3b388 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 @@ -37,16 +37,17 @@ import org.springframework.util.ClassUtils; /** * Convenience class for building a {@link DataSource} with common implementations and - * properties. If HikariCP, Tomcat or Commons DBCP are on the classpath one of them will - * be selected (in that order with Hikari first). In the interest of a uniform interface, - * and so that there can be a fallback to an embedded database if one can be detected on - * the classpath, only a small set of common configuration properties are supported. To - * inject additional properties into the result you can downcast it, or use + * properties. If HikariCP, Tomcat, Commons DBCP or Oracle UCP are on the classpath one of + * them will be selected (in that order with Hikari first). In the interest of a uniform + * interface, and so that there can be a fallback to an embedded database if one can be + * detected on the classpath, only a small set of common configuration properties are + * supported. To inject additional properties into the result you can downcast it, or use * {@code @ConfigurationProperties}. * * @param type of DataSource produced by the builder * @author Dave Syer * @author Madhura Bhave + * @author Fabio Grassi * @since 2.0.0 */ public final class DataSourceBuilder { @@ -166,9 +167,9 @@ public final class DataSourceBuilder { } - private static class OracleDataSourceSettings extends DataSourceSettings { + private static class OracleCommonDataSourceSettings extends DataSourceSettings { - OracleDataSourceSettings(Class type) { + OracleCommonDataSourceSettings(Class type) { super(type, (aliases) -> aliases.addAliases("username", "user")); } @@ -194,7 +195,7 @@ public final class DataSourceBuilder { (type) -> new DataSourceSettings(type, (aliases) -> aliases.addAliases("driver-class-name", "driver-class")))); addIfAvailable(this.allDataSourceSettings, create(classLoader, - "oracle.jdbc.datasource.OracleCommonDataSource", OracleDataSourceSettings::new)); + "oracle.jdbc.datasource.OracleCommonDataSource", OracleCommonDataSourceSettings::new)); } private static List resolveAvailableDataSourceSettings(ClassLoader classLoader) { @@ -205,6 +206,8 @@ public final class DataSourceBuilder { create(classLoader, "org.apache.tomcat.jdbc.pool.DataSource", DataSourceSettings::new)); addIfAvailable(providers, create(classLoader, "org.apache.commons.dbcp2.BasicDataSource", DataSourceSettings::new)); + addIfAvailable(providers, + create(classLoader, "oracle.ucp.jdbc.PoolDataSourceImpl", DataSourceSettings::new)); return providers; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadata.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadata.java new file mode 100644 index 0000000000..d2f24bd185 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadata.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2019 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.jdbc.metadata; + +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.util.StringUtils; + +import oracle.ucp.jdbc.PoolDataSource; + +/** + * {@link DataSourcePoolMetadata} for a Oracle UCP {@link DataSource}. + * + * @author Fabio Grassi + * @since 2.3.4 + */ +public class UcpDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata { + + public UcpDataSourcePoolMetadata(PoolDataSource dataSource) { + super(dataSource); + } + + @Override + public Integer getActive() { + try { + return getDataSource().getBorrowedConnectionsCount(); + } + catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error while reading property borrowedConnectionsCount", se); + } + } + + @Override + public Integer getIdle() { + try { + return getDataSource().getAvailableConnectionsCount(); + } + catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error while reading property availableConnectionsCount", se); + } + } + + @Override + public Integer getMax() { + return getDataSource().getMaxPoolSize(); + } + + @Override + public Integer getMin() { + return getDataSource().getMinPoolSize(); + } + + @Override + public String getValidationQuery() { + return getDataSource().getSQLForValidateConnection(); + } + + @Override + public Boolean getDefaultAutoCommit() { + String ac = getDataSource().getConnectionProperty("autoCommit"); + return StringUtils.hasText(ac) ? Boolean.valueOf(ac) : null; + } + +} 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 779a02be42..8e59a40a30 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 @@ -16,6 +16,8 @@ package org.springframework.boot.jdbc; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.Closeable; import java.io.IOException; import java.net.URL; @@ -31,15 +33,17 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.h2.Driver; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; - import org.springframework.jdbc.datasource.SimpleDriverDataSource; -import static org.assertj.core.api.Assertions.assertThat; +import com.zaxxer.hikari.HikariDataSource; + +import oracle.ucp.jdbc.PoolDataSourceImpl; /** * Tests for {@link DataSourceBuilder}. * * @author Stephane Nicoll + * @author Fabio Grassi */ class DataSourceBuilderTests { @@ -68,13 +72,20 @@ class DataSourceBuilderTests { } @Test - void defaultToCommonsDbcp2AsLastResort() { + void defaultToCommonsDbcp2IfNeitherHikariNorTomcatIsNotAvailable() { this.dataSource = DataSourceBuilder .create(new HidePackagesClassLoader("com.zaxxer.hikari", "org.apache.tomcat.jdbc.pool")) .url("jdbc:h2:test").build(); assertThat(this.dataSource).isInstanceOf(BasicDataSource.class); } + @Test + void defaultToUcpAsLastResort() { + this.dataSource = DataSourceBuilder.create(new HidePackagesClassLoader("com.zaxxer.hikari", + "org.apache.tomcat.jdbc.pool", "org.apache.commons.dbcp2")).url("jdbc:h2:test").build(); + assertThat(this.dataSource).isInstanceOf(PoolDataSourceImpl.class); + } + @Test void specificTypeOfDataSource() { HikariDataSource hikariDataSource = DataSourceBuilder.create().type(HikariDataSource.class).build(); @@ -114,6 +125,15 @@ class DataSourceBuilderTests { assertThat(testDataSource.getDriverClass()).isNull(); } + @Test + void UcpCanBeCreatedByDriverClassNamee() { + this.dataSource = DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver") + .type(PoolDataSourceImpl.class).build(); + assertThat(this.dataSource).isInstanceOf(PoolDataSourceImpl.class); + assertThat(((PoolDataSourceImpl) this.dataSource).getConnectionFactoryClassName()) + .isEqualTo("org.hsqldb.jdbc.JDBCDriver"); + } + final class HidePackagesClassLoader extends URLClassLoader { private final String[] hiddenPackages; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadataTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadataTests.java new file mode 100644 index 0000000000..5774427231 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/metadata/UcpDataSourcePoolMetadataTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2019 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.jdbc.metadata; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; + +import org.springframework.dao.InvalidDataAccessApiUsageException; + +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceImpl; + +/** + * Tests for {@link UcpDataSourcePoolMetadata}. + * + * @author Fabio Grassi + */ +public class UcpDataSourcePoolMetadataTests extends AbstractDataSourcePoolMetadataTests { + + private final UcpDataSourcePoolMetadata dataSourceMetadata = new UcpDataSourcePoolMetadata(createDataSource(0, 2)); + + @Override + protected UcpDataSourcePoolMetadata getDataSourceMetadata() { + return this.dataSourceMetadata; + } + + @Override + public void getValidationQuery() { + PoolDataSource dataSource = createDataSource(0, 4); + try { + dataSource.setSQLForValidateConnection("SELECT NULL FROM DUAL"); + } catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error while setting property SQLForValidateConnection", se); + } + assertThat(new UcpDataSourcePoolMetadata(dataSource).getValidationQuery()).isEqualTo("SELECT NULL FROM DUAL"); + } + + @Override + public void getDefaultAutoCommit() { + PoolDataSource dataSource = createDataSource(0, 4); + try { + dataSource.setConnectionProperty("autoCommit", "false"); + } catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error while setting property connectionProperties.autoCommit", se); + } + assertThat(new UcpDataSourcePoolMetadata(dataSource).getDefaultAutoCommit()).isFalse(); + } + + private PoolDataSource createDataSource(int minSize, int maxSize) { + PoolDataSource dataSource = initializeBuilder().type(PoolDataSourceImpl.class).build(); + try { + dataSource.setInitialPoolSize(minSize); + dataSource.setMinPoolSize(minSize); + dataSource.setMaxPoolSize(maxSize); + } catch (SQLException se) { + throw new InvalidDataAccessApiUsageException("Error while setting a property", se); + } + return dataSource; + } + +}