mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Add ConnectionDetail support to JDBC auto-configuration
Update JDBC auto-configuration so that `JdbcConnectionDetails` beans may be optionally used to provide connection details. See gh-34657 Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com> Co-Authored-By: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
parent
aa91f2b8b6
commit
d09ac00824
@ -236,6 +236,7 @@ dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.mockito:mockito-core")
|
||||
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||
testImplementation("org.postgresql:postgresql")
|
||||
testImplementation("org.skyscreamer:jsonassert")
|
||||
testImplementation("org.springframework:spring-test")
|
||||
testImplementation("org.springframework:spring-core-test")
|
||||
|
@ -47,6 +47,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayDataSourceCondition;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
|
||||
@ -86,6 +87,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Semyon Danilov
|
||||
* @author Chris Bono
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
|
||||
@ -125,7 +127,7 @@ public class FlywayAutoConfiguration {
|
||||
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
|
||||
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks) {
|
||||
return flyway(properties, resourceLoader, dataSource, flywayDataSource, fluentConfigurationCustomizers,
|
||||
javaMigrations, callbacks, new ResourceProviderCustomizer());
|
||||
javaMigrations, callbacks, new ResourceProviderCustomizer(), null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -133,9 +135,15 @@ public class FlywayAutoConfiguration {
|
||||
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
|
||||
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
|
||||
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks,
|
||||
ResourceProviderCustomizer resourceProviderCustomizer) {
|
||||
ResourceProviderCustomizer resourceProviderCustomizer,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader());
|
||||
configureDataSource(configuration, properties, flywayDataSource.getIfAvailable(), dataSource.getIfUnique());
|
||||
JdbcConnectionDetails connectionDetails = (connectionDetailsProvider != null)
|
||||
? connectionDetailsProvider.getIfAvailable() : null;
|
||||
connectionDetails = (connectionDetails != null) ? connectionDetails
|
||||
: new FlywayPropertiesJdbcConnectionDetails(properties);
|
||||
configureDataSource(configuration, flywayDataSource.getIfAvailable(), dataSource.getIfUnique(),
|
||||
connectionDetails);
|
||||
configureProperties(configuration, properties);
|
||||
configureCallbacks(configuration, callbacks.orderedStream().toList());
|
||||
configureJavaMigrations(configuration, javaMigrations.orderedStream().toList());
|
||||
@ -144,38 +152,41 @@ public class FlywayAutoConfiguration {
|
||||
return configuration.load();
|
||||
}
|
||||
|
||||
private void configureDataSource(FluentConfiguration configuration, FlywayProperties properties,
|
||||
DataSource flywayDataSource, DataSource dataSource) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(properties, flywayDataSource, dataSource);
|
||||
private void configureDataSource(FluentConfiguration configuration, DataSource flywayDataSource,
|
||||
DataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(flywayDataSource, dataSource, connectionDetails);
|
||||
configuration.dataSource(migrationDataSource);
|
||||
}
|
||||
|
||||
private DataSource getMigrationDataSource(FlywayProperties properties, DataSource flywayDataSource,
|
||||
DataSource dataSource) {
|
||||
private DataSource getMigrationDataSource(DataSource flywayDataSource, DataSource dataSource,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
if (flywayDataSource != null) {
|
||||
return flywayDataSource;
|
||||
}
|
||||
if (properties.getUrl() != null) {
|
||||
String url = connectionDetails.getJdbcUrl();
|
||||
if (url != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.create().type(SimpleDriverDataSource.class);
|
||||
builder.url(properties.getUrl());
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
builder.url(url);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
if (properties.getUser() != null && dataSource != null) {
|
||||
String user = connectionDetails.getUsername();
|
||||
if (user != null && dataSource != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
|
||||
.type(SimpleDriverDataSource.class);
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
Assert.state(dataSource != null, "Flyway migration DataSource missing");
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void applyCommonBuilderProperties(FlywayProperties properties, DataSourceBuilder<?> builder) {
|
||||
builder.username(properties.getUser());
|
||||
builder.password(properties.getPassword());
|
||||
if (StringUtils.hasText(properties.getDriverClassName())) {
|
||||
builder.driverClassName(properties.getDriverClassName());
|
||||
private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder<?> builder) {
|
||||
builder.username(connectionDetails.getUsername());
|
||||
builder.password(connectionDetails.getPassword());
|
||||
String driverClassName = connectionDetails.getDriverClassName();
|
||||
if (StringUtils.hasText(driverClassName)) {
|
||||
builder.driverClassName(driverClassName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,6 +384,11 @@ public class FlywayAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
private static final class JdbcConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.flyway", name = "url")
|
||||
private static final class FlywayUrlCondition {
|
||||
|
||||
@ -389,4 +405,37 @@ public class FlywayAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link FlywayProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static final class FlywayPropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final FlywayProperties properties;
|
||||
|
||||
private FlywayPropertiesJdbcConnectionDetails(FlywayProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return this.properties.getDriverClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@ -24,10 +24,13 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import oracle.jdbc.OracleConnection;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -40,6 +43,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Fabio Grassi
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class DataSourceConfiguration {
|
||||
|
||||
@ -48,6 +53,17 @@ abstract class DataSourceConfiguration {
|
||||
return (T) properties.initializeDataSourceBuilder().type(type).build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> T createDataSource(JdbcConnectionDetails connectionDetails, Class<? extends DataSource> type,
|
||||
ClassLoader classLoader) {
|
||||
return (T) DataSourceBuilder.create(classLoader)
|
||||
.url(connectionDetails.getJdbcUrl())
|
||||
.username(connectionDetails.getUsername())
|
||||
.password(connectionDetails.getPassword())
|
||||
.type(type)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomcat Pool DataSource configuration.
|
||||
*/
|
||||
@ -58,13 +74,26 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Tomcat {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static TomcatJdbcConnectionDetailsBeanPostProcessor tomcatJdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new TomcatJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
|
||||
org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
|
||||
String validationQuery = databaseDriver.getValidationQuery();
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
Class<? extends DataSource> dataSourceType = org.apache.tomcat.jdbc.pool.DataSource.class;
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader())
|
||||
: createDataSource(properties, dataSourceType);
|
||||
String validationQuery;
|
||||
String url = (connectionDetails != null) ? connectionDetails.getJdbcUrl() : properties.determineUrl();
|
||||
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(url);
|
||||
validationQuery = databaseDriver.getValidationQuery();
|
||||
if (validationQuery != null) {
|
||||
dataSource.setTestOnBorrow(true);
|
||||
dataSource.setValidationQuery(validationQuery);
|
||||
@ -84,10 +113,21 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Hikari {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static HikariJdbcConnectionDetailsBeanPostProcessor jdbcConnectionDetailsHikariBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new HikariJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.hikari")
|
||||
HikariDataSource dataSource(DataSourceProperties properties) {
|
||||
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
|
||||
HikariDataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
HikariDataSource dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, HikariDataSource.class, properties.getClassLoader())
|
||||
: createDataSource(properties, HikariDataSource.class);
|
||||
if (StringUtils.hasText(properties.getName())) {
|
||||
dataSource.setPoolName(properties.getName());
|
||||
}
|
||||
@ -106,10 +146,22 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class Dbcp2 {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static Dbcp2JdbcConnectionDetailsBeanPostProcessor dbcp2JdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new Dbcp2JdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
|
||||
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
|
||||
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
|
||||
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
Class<? extends DataSource> dataSourceType = org.apache.commons.dbcp2.BasicDataSource.class;
|
||||
return (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, dataSourceType, properties.getClassLoader())
|
||||
: createDataSource(properties, dataSourceType);
|
||||
}
|
||||
|
||||
}
|
||||
@ -124,10 +176,21 @@ abstract class DataSourceConfiguration {
|
||||
matchIfMissing = true)
|
||||
static class OracleUcp {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
static OracleUcpJdbcConnectionDetailsBeanPostProcessor oracleUcpJdbcConnectionDetailsBeanPostProcessor(
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
return new OracleUcpJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.oracleucp")
|
||||
PoolDataSourceImpl dataSource(DataSourceProperties properties) throws SQLException {
|
||||
PoolDataSourceImpl dataSource = createDataSource(properties, PoolDataSourceImpl.class);
|
||||
PoolDataSourceImpl dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) throws SQLException {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
PoolDataSourceImpl dataSource = (connectionDetails != null)
|
||||
? createDataSource(connectionDetails, PoolDataSourceImpl.class, properties.getClassLoader())
|
||||
: createDataSource(properties, PoolDataSourceImpl.class);
|
||||
dataSource.setValidateConnectionOnBorrow(true);
|
||||
if (StringUtils.hasText(properties.getName())) {
|
||||
dataSource.setConnectionPoolName(properties.getName());
|
||||
@ -146,7 +209,17 @@ abstract class DataSourceConfiguration {
|
||||
static class Generic {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource(DataSourceProperties properties) {
|
||||
DataSource dataSource(DataSourceProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
JdbcConnectionDetails connectionDetails = connectionDetailsProvider.getIfAvailable();
|
||||
if (connectionDetails != null) {
|
||||
return DataSourceBuilder.create(properties.getClassLoader())
|
||||
.url(connectionDetails.getJdbcUrl())
|
||||
.username(connectionDetails.getUsername())
|
||||
.password(connectionDetails.getPassword())
|
||||
.type(properties.getType())
|
||||
.build();
|
||||
}
|
||||
return properties.initializeDataSourceBuilder().build();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 org.apache.commons.dbcp2.BasicDataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link BasicDataSource} and name 'dataSource' to apply the
|
||||
* values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Dbcp2JdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<BasicDataSource> {
|
||||
|
||||
Dbcp2JdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(BasicDataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(BasicDataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link HikariDataSource} and name 'dataSource' to apply
|
||||
* the values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariJdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<HikariDataSource> {
|
||||
|
||||
HikariJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(HikariDataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(HikariDataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setJdbcUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to an SQL service using JDBC.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface JdbcConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Hostname for the database.
|
||||
* @return the username for the database
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Password for the database.
|
||||
* @return the password for the database
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
/**
|
||||
* JDBC url for the database.
|
||||
* @return the JDBC url for the database
|
||||
*/
|
||||
String getJdbcUrl();
|
||||
|
||||
/**
|
||||
* The name of the JDBC driver class. Defaults to the class name of the driver
|
||||
* specified in the JDBC URL.
|
||||
* @return the JDBC driver class name
|
||||
* @see #getJdbcUrl()
|
||||
* @see DatabaseDriver#fromJdbcUrl(String)
|
||||
* @see DatabaseDriver#getDriverClassName()
|
||||
*/
|
||||
default String getDriverClassName() {
|
||||
return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getDriverClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the XA DataSource class. Defaults to the class name from the
|
||||
* driver specified in the JDBC URL.
|
||||
* @return the XA DataSource class name
|
||||
* @see #getJdbcUrl()
|
||||
* @see DatabaseDriver#fromJdbcUrl(String)
|
||||
* @see DatabaseDriver#getXaDataSourceClassName()
|
||||
*/
|
||||
default String getXaDataSourceClassName() {
|
||||
return DatabaseDriver.fromJdbcUrl(getJdbcUrl()).getXaDataSourceClassName();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
/**
|
||||
* Abstract base class for DataSource bean post processors which apply values from
|
||||
* {@link JdbcConnectionDetails}. Acts on beans named 'dataSource' of type {@code T}.
|
||||
*
|
||||
* @param <T> type of the datasource
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class JdbcConnectionDetailsBeanPostProcessor<T> implements BeanPostProcessor, PriorityOrdered {
|
||||
|
||||
private final Class<T> dataSourceClass;
|
||||
|
||||
private final ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider;
|
||||
|
||||
JdbcConnectionDetailsBeanPostProcessor(Class<T> dataSourceClass,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
this.dataSourceClass = dataSourceClass;
|
||||
this.connectionDetailsProvider = connectionDetailsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (this.dataSourceClass.isAssignableFrom(bean.getClass()) && "dataSource".equals(beanName)) {
|
||||
JdbcConnectionDetails connectionDetails = this.connectionDetailsProvider.getObject();
|
||||
return processDataSource((T) bean, connectionDetails);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
protected abstract Object processDataSource(T dataSource, JdbcConnectionDetails connectionDetails);
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// Runs after ConfigurationPropertiesBindingPostProcessor
|
||||
return Ordered.HIGHEST_PRECEDENCE + 2;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 java.sql.SQLException;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link PoolDataSourceImpl} and name 'dataSource' to apply
|
||||
* the values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpJdbcConnectionDetailsBeanPostProcessor
|
||||
extends JdbcConnectionDetailsBeanPostProcessor<PoolDataSourceImpl> {
|
||||
|
||||
OracleUcpJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(PoolDataSourceImpl.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(PoolDataSourceImpl dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
try {
|
||||
dataSource.setURL(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUser(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setConnectionFactoryClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new RuntimeException("Failed to set URL / user / password of datasource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 org.apache.tomcat.jdbc.pool.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
||||
/**
|
||||
* Post-processes beans of type {@link DataSource} and name 'dataSource' to apply the
|
||||
* values from {@link JdbcConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatJdbcConnectionDetailsBeanPostProcessor extends JdbcConnectionDetailsBeanPostProcessor<DataSource> {
|
||||
|
||||
TomcatJdbcConnectionDetailsBeanPostProcessor(ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
|
||||
super(DataSource.class, connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object processDataSource(DataSource dataSource, JdbcConnectionDetails connectionDetails) {
|
||||
dataSource.setUrl(connectionDetails.getJdbcUrl());
|
||||
dataSource.setUsername(connectionDetails.getUsername());
|
||||
dataSource.setPassword(connectionDetails.getPassword());
|
||||
dataSource.setDriverClassName(connectionDetails.getDriverClassName());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* 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.
|
||||
@ -40,7 +40,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
@ -54,6 +53,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Phillip Webb
|
||||
* @author Josh Long
|
||||
* @author Madhura Bhave
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@AutoConfiguration(before = DataSourceAutoConfiguration.class)
|
||||
@ -67,8 +68,10 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource(XADataSourceWrapper wrapper, DataSourceProperties properties,
|
||||
ObjectProvider<XADataSource> xaDataSource) throws Exception {
|
||||
return wrapper.wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties)));
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetails, ObjectProvider<XADataSource> xaDataSource)
|
||||
throws Exception {
|
||||
return wrapper.wrapDataSource(xaDataSource.getIfAvailable(() -> createXaDataSource(properties,
|
||||
connectionDetails.getIfAvailable(() -> new PropertiesJdbcConnectionDetails(properties)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,14 +79,11 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
private XADataSource createXaDataSource(DataSourceProperties properties) {
|
||||
String className = properties.getXa().getDataSourceClassName();
|
||||
if (!StringUtils.hasLength(className)) {
|
||||
className = DatabaseDriver.fromJdbcUrl(properties.determineUrl()).getXaDataSourceClassName();
|
||||
}
|
||||
private XADataSource createXaDataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
|
||||
String className = connectionDetails.getXaDataSourceClassName();
|
||||
Assert.state(StringUtils.hasLength(className), "No XA DataSource class name specified");
|
||||
XADataSource dataSource = createXaDataSourceInstance(className);
|
||||
bindXaProperties(dataSource, properties);
|
||||
bindXaProperties(dataSource, properties, connectionDetails);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@ -99,18 +99,19 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindXaProperties(XADataSource target, DataSourceProperties dataSourceProperties) {
|
||||
Binder binder = new Binder(getBinderSource(dataSourceProperties));
|
||||
private void bindXaProperties(XADataSource target, DataSourceProperties dataSourceProperties,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
Binder binder = new Binder(getBinderSource(dataSourceProperties, connectionDetails));
|
||||
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(target));
|
||||
}
|
||||
|
||||
private ConfigurationPropertySource getBinderSource(DataSourceProperties dataSourceProperties) {
|
||||
Map<Object, Object> properties = new HashMap<>();
|
||||
properties.putAll(dataSourceProperties.getXa().getProperties());
|
||||
properties.computeIfAbsent("user", (key) -> dataSourceProperties.determineUsername());
|
||||
properties.computeIfAbsent("password", (key) -> dataSourceProperties.determinePassword());
|
||||
private ConfigurationPropertySource getBinderSource(DataSourceProperties dataSourceProperties,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
Map<Object, Object> properties = new HashMap<>(dataSourceProperties.getXa().getProperties());
|
||||
properties.computeIfAbsent("user", (key) -> connectionDetails.getUsername());
|
||||
properties.computeIfAbsent("password", (key) -> connectionDetails.getPassword());
|
||||
try {
|
||||
properties.computeIfAbsent("url", (key) -> dataSourceProperties.determineUrl());
|
||||
properties.computeIfAbsent("url", (key) -> connectionDetails.getJdbcUrl());
|
||||
}
|
||||
catch (DataSourceBeanCreationException ex) {
|
||||
// Continue as not all XA DataSource's require a URL
|
||||
@ -121,4 +122,45 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
return source.withAliases(aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link DataSourceProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static class PropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final DataSourceProperties properties;
|
||||
|
||||
PropertiesJdbcConnectionDetails(DataSourceProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.determineUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.determinePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.determineUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return (this.properties.getDriverClassName() != null) ? this.properties.getDriverClassName()
|
||||
: JdbcConnectionDetails.super.getDriverClassName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXaDataSourceClassName() {
|
||||
return (this.properties.getXa().getDataSourceClassName() != null)
|
||||
? this.properties.getXa().getDataSourceClassName()
|
||||
: JdbcConnectionDetails.super.getXaDataSourceClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ 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.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseDataSourceCondition;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
@ -61,6 +62,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author András Deák
|
||||
* @author Ferenc Gratzer
|
||||
* @author Evgeniy Cheban
|
||||
* @author Moritz Halbritter
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
|
||||
@ -83,38 +85,34 @@ public class LiquibaseAutoConfiguration {
|
||||
@EnableConfigurationProperties(LiquibaseProperties.class)
|
||||
public static class LiquibaseConfiguration {
|
||||
|
||||
private final LiquibaseProperties properties;
|
||||
|
||||
public LiquibaseConfiguration(LiquibaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringLiquibase liquibase(ObjectProvider<DataSource> dataSource,
|
||||
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) {
|
||||
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource, LiquibaseProperties properties,
|
||||
ObjectProvider<JdbcConnectionDetails> connectionDetails) {
|
||||
SpringLiquibase liquibase = createSpringLiquibase(liquibaseDataSource.getIfAvailable(),
|
||||
dataSource.getIfUnique());
|
||||
liquibase.setChangeLog(this.properties.getChangeLog());
|
||||
liquibase.setClearCheckSums(this.properties.isClearChecksums());
|
||||
liquibase.setContexts(this.properties.getContexts());
|
||||
liquibase.setDefaultSchema(this.properties.getDefaultSchema());
|
||||
liquibase.setLiquibaseSchema(this.properties.getLiquibaseSchema());
|
||||
liquibase.setLiquibaseTablespace(this.properties.getLiquibaseTablespace());
|
||||
liquibase.setDatabaseChangeLogTable(this.properties.getDatabaseChangeLogTable());
|
||||
liquibase.setDatabaseChangeLogLockTable(this.properties.getDatabaseChangeLogLockTable());
|
||||
liquibase.setDropFirst(this.properties.isDropFirst());
|
||||
liquibase.setShouldRun(this.properties.isEnabled());
|
||||
liquibase.setLabelFilter(this.properties.getLabelFilter());
|
||||
liquibase.setChangeLogParameters(this.properties.getParameters());
|
||||
liquibase.setRollbackFile(this.properties.getRollbackFile());
|
||||
liquibase.setTestRollbackOnUpdate(this.properties.isTestRollbackOnUpdate());
|
||||
liquibase.setTag(this.properties.getTag());
|
||||
dataSource.getIfUnique(),
|
||||
connectionDetails.getIfAvailable(() -> new LiquibasePropertiesJdbcConnectionDetails(properties)));
|
||||
liquibase.setChangeLog(properties.getChangeLog());
|
||||
liquibase.setClearCheckSums(properties.isClearChecksums());
|
||||
liquibase.setContexts(properties.getContexts());
|
||||
liquibase.setDefaultSchema(properties.getDefaultSchema());
|
||||
liquibase.setLiquibaseSchema(properties.getLiquibaseSchema());
|
||||
liquibase.setLiquibaseTablespace(properties.getLiquibaseTablespace());
|
||||
liquibase.setDatabaseChangeLogTable(properties.getDatabaseChangeLogTable());
|
||||
liquibase.setDatabaseChangeLogLockTable(properties.getDatabaseChangeLogLockTable());
|
||||
liquibase.setDropFirst(properties.isDropFirst());
|
||||
liquibase.setShouldRun(properties.isEnabled());
|
||||
liquibase.setLabelFilter(properties.getLabelFilter());
|
||||
liquibase.setChangeLogParameters(properties.getParameters());
|
||||
liquibase.setRollbackFile(properties.getRollbackFile());
|
||||
liquibase.setTestRollbackOnUpdate(properties.isTestRollbackOnUpdate());
|
||||
liquibase.setTag(properties.getTag());
|
||||
return liquibase;
|
||||
}
|
||||
|
||||
private SpringLiquibase createSpringLiquibase(DataSource liquibaseDataSource, DataSource dataSource) {
|
||||
LiquibaseProperties properties = this.properties;
|
||||
DataSource migrationDataSource = getMigrationDataSource(liquibaseDataSource, dataSource, properties);
|
||||
private SpringLiquibase createSpringLiquibase(DataSource liquibaseDataSource, DataSource dataSource,
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
DataSource migrationDataSource = getMigrationDataSource(liquibaseDataSource, dataSource, connectionDetails);
|
||||
SpringLiquibase liquibase = (migrationDataSource == liquibaseDataSource
|
||||
|| migrationDataSource == dataSource) ? new SpringLiquibase()
|
||||
: new DataSourceClosingSpringLiquibase();
|
||||
@ -123,31 +121,34 @@ public class LiquibaseAutoConfiguration {
|
||||
}
|
||||
|
||||
private DataSource getMigrationDataSource(DataSource liquibaseDataSource, DataSource dataSource,
|
||||
LiquibaseProperties properties) {
|
||||
JdbcConnectionDetails connectionDetails) {
|
||||
if (liquibaseDataSource != null) {
|
||||
return liquibaseDataSource;
|
||||
}
|
||||
if (properties.getUrl() != null) {
|
||||
String url = connectionDetails.getJdbcUrl();
|
||||
if (url != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.create().type(SimpleDriverDataSource.class);
|
||||
builder.url(properties.getUrl());
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
builder.url(url);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
if (properties.getUser() != null && dataSource != null) {
|
||||
String user = connectionDetails.getUsername();
|
||||
if (user != null && dataSource != null) {
|
||||
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
|
||||
.type(SimpleDriverDataSource.class);
|
||||
applyCommonBuilderProperties(properties, builder);
|
||||
applyConnectionDetails(connectionDetails, builder);
|
||||
return builder.build();
|
||||
}
|
||||
Assert.state(dataSource != null, "Liquibase migration DataSource missing");
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void applyCommonBuilderProperties(LiquibaseProperties properties, DataSourceBuilder<?> builder) {
|
||||
builder.username(properties.getUser());
|
||||
builder.password(properties.getPassword());
|
||||
if (StringUtils.hasText(properties.getDriverClassName())) {
|
||||
builder.driverClassName(properties.getDriverClassName());
|
||||
private void applyConnectionDetails(JdbcConnectionDetails connectionDetails, DataSourceBuilder<?> builder) {
|
||||
builder.username(connectionDetails.getUsername());
|
||||
builder.password(connectionDetails.getPassword());
|
||||
String driverClassName = connectionDetails.getDriverClassName();
|
||||
if (StringUtils.hasText(driverClassName)) {
|
||||
builder.driverClassName(driverClassName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,6 +165,11 @@ public class LiquibaseAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(JdbcConnectionDetails.class)
|
||||
private static final class JdbcConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.liquibase", name = "url")
|
||||
private static final class LiquibaseUrlCondition {
|
||||
|
||||
@ -180,4 +186,37 @@ public class LiquibaseAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link LiquibaseProperties} to {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
private static final class LiquibasePropertiesJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
private final LiquibaseProperties properties;
|
||||
|
||||
private LiquibasePropertiesJdbcConnectionDetails(LiquibaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return this.properties.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return this.properties.getDriverClassName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayAutoConfigurationRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.jdbc.SchemaManagement;
|
||||
@ -120,6 +121,16 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsDataSourceWithNoDataSourceBeanAndJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(JdbcConnectionDetailsConfiguration.class, MockFlywayMigrationStrategy.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWithFlywayUrlAndNoSpringJdbc() {
|
||||
this.contextRunner.withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:" + UUID.randomUUID())
|
||||
@ -137,6 +148,28 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDataSourceWithJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class,
|
||||
MockFlywayMigrationStrategy.class)
|
||||
.withPropertyValues("spring.flyway.url=jdbc:hsqldb:mem:flywaytest", "spring.flyway.user=some-user",
|
||||
"spring.flyway.password=some-password",
|
||||
"spring.flyway.driver-class-name=org.hsqldb.jdbc.JDBCDriver")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
Flyway flyway = context.getBean(Flyway.class);
|
||||
DataSource dataSource = flyway.getConfiguration().getDataSource();
|
||||
assertThat(dataSource).isInstanceOf(SimpleDriverDataSource.class);
|
||||
SimpleDriverDataSource simpleDriverDataSource = (SimpleDriverDataSource) dataSource;
|
||||
assertThat(simpleDriverDataSource.getUrl())
|
||||
.isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(simpleDriverDataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(simpleDriverDataSource.getPassword()).isEqualTo("secret-1");
|
||||
assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(org.postgresql.Driver.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDataSourceWithUser() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
@ -200,6 +233,19 @@ class FlywayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void flywayDataSourceIsUsedWhenJdbcConnectionDetailsIsAvailable() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(FlywayDataSourceConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
JdbcConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JdbcConnectionDetails.class);
|
||||
assertThat(context).hasSingleBean(Flyway.class);
|
||||
assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource())
|
||||
.isEqualTo(context.getBean("flywayDataSource"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void flywayDataSourceWithoutDataSourceAutoConfiguration() {
|
||||
this.contextRunner.withUserConfiguration(FlywayDataSourceConfiguration.class).run((context) -> {
|
||||
@ -1036,4 +1082,31 @@ class FlywayAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new JdbcConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://database.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
@ -60,6 +61,9 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DataSourceAutoConfigurationTests {
|
||||
|
||||
@ -244,6 +248,41 @@ class DataSourceAutoConfigurationTests {
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(DataSourceScriptDatabaseInitializer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void dbcp2UsesJdbcConnectionDetailsIfAvailable() {
|
||||
ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource",
|
||||
"spring.datasource.dbcp2.url=jdbc:broken", "spring.datasource.dbcp2.username=alice",
|
||||
"spring.datasource.dbcp2.password=secret")
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class));
|
||||
runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(BasicDataSource.class))
|
||||
.satisfies((dbcp2) -> {
|
||||
assertThat(dbcp2.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dbcp2.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dbcp2.getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(dbcp2.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericUsesJdbcConnectionDetailsIfAvailable() {
|
||||
ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.datasource.type=" + TestDataSource.class.getName())
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class));
|
||||
runner.withUserConfiguration(JdbcConnectionDetailsConfiguration.class).run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(TestDataSource.class);
|
||||
TestDataSource source = (TestDataSource) dataSource;
|
||||
assertThat(source.getUsername()).isEqualTo("user-1");
|
||||
assertThat(source.getPassword()).isEqualTo("password-1");
|
||||
assertThat(source.getDriver().getClass().getName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(source.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
private static Function<ApplicationContextRunner, ApplicationContextRunner> hideConnectionPools() {
|
||||
return (runner) -> runner.withClassLoader(new FilteredClassLoader("org.apache.tomcat", "com.zaxxer.hikari",
|
||||
"org.apache.commons.dbcp2", "oracle.ucp.jdbc", "com.mchange"));
|
||||
@ -259,6 +298,16 @@ class DataSourceAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails sqlJdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestDataSourceConfiguration {
|
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Dbcp2JdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Dbcp2JdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName("will-be-overwritten");
|
||||
new Dbcp2JdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getDriverClassName()).isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
@ -19,10 +19,13 @@ package org.springframework.boot.autoconfigure.jdbc;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ -31,9 +34,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariDataSourceConfigurationTests {
|
||||
|
||||
private static final String PREFIX = "spring.datasource.hikari.";
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName());
|
||||
@ -49,8 +57,7 @@ class HikariDataSourceConfigurationTests {
|
||||
@Test
|
||||
void testDataSourcePropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.hikari.jdbc-url=jdbc:foo//bar/spam",
|
||||
"spring.datasource.hikari.max-lifetime=1234")
|
||||
.withPropertyValues(PREFIX + "jdbc-url=jdbc:foo//bar/spam", "spring.datasource.hikari.max-lifetime=1234")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getJdbcUrl()).isEqualTo("jdbc:foo//bar/spam");
|
||||
@ -61,8 +68,7 @@ class HikariDataSourceConfigurationTests {
|
||||
@Test
|
||||
void testDataSourceGenericPropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.datasource.hikari.data-source-properties.dataSourceClassName=org.h2.JDBCDataSource")
|
||||
.withPropertyValues(PREFIX + "data-source-properties.dataSourceClassName=org.h2.JDBCDataSource")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getDataSourceProperties().getProperty("dataSourceClassName"))
|
||||
@ -90,12 +96,38 @@ class HikariDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void poolNameTakesPrecedenceOverName() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.name=myDS", "spring.datasource.hikari.pool-name=myHikariDS")
|
||||
this.contextRunner.withPropertyValues("spring.datasource.name=myDS", PREFIX + "pool-name=myHikariDS")
|
||||
.run((context) -> {
|
||||
HikariDataSource ds = context.getBean(HikariDataSource.class);
|
||||
assertThat(ds.getPoolName()).isEqualTo("myHikariDS");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).asInstanceOf(InstanceOfAssertFactories.type(HikariDataSource.class))
|
||||
.satisfies((hikari) -> {
|
||||
assertThat(hikari.getUsername()).isEqualTo("user-1");
|
||||
assertThat(hikari.getPassword()).isEqualTo("password-1");
|
||||
assertThat(hikari.getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(hikari.getJdbcUrl())
|
||||
.isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails sqlConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HikariJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HikariJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
HikariDataSource dataSource = new HikariDataSource();
|
||||
dataSource.setJdbcUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName(DatabaseDriver.H2.getDriverClassName());
|
||||
new HikariJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getJdbcUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getDriverClassName()).isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
@ -22,10 +22,13 @@ import javax.sql.DataSource;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSource;
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import oracle.ucp.util.OpaqueString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ -34,9 +37,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Fabio Grassi
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
private static final String PREFIX = "spring.datasource.oracleucp.";
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + PoolDataSource.class.getName());
|
||||
@ -54,9 +62,7 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void testDataSourcePropertiesOverridden() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.oracleucp.url=jdbc:foo//bar/spam",
|
||||
"spring.datasource.oracleucp.max-idle-time=1234")
|
||||
this.contextRunner.withPropertyValues(PREFIX + "url=jdbc:foo//bar/spam", PREFIX + "max-idle-time=1234")
|
||||
.run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getURL()).isEqualTo("jdbc:foo//bar/spam");
|
||||
@ -66,8 +72,7 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
|
||||
@Test
|
||||
void testDataSourceConnectionPropertiesOverridden() {
|
||||
this.contextRunner.withPropertyValues("spring.datasource.oracleucp.connection-properties.autoCommit=false")
|
||||
.run((context) -> {
|
||||
this.contextRunner.withPropertyValues(PREFIX + "connection-properties.autoCommit=false").run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getConnectionProperty("autoCommit")).isEqualTo("false");
|
||||
});
|
||||
@ -100,12 +105,38 @@ class OracleUcpDataSourceConfigurationTests {
|
||||
@Test
|
||||
void poolNameTakesPrecedenceOverName() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.datasource.name=myDS",
|
||||
"spring.datasource.oracleucp.connection-pool-name=myOracleUcpDS")
|
||||
.withPropertyValues("spring.datasource.name=myDS", PREFIX + "connection-pool-name=myOracleUcpDS")
|
||||
.run((context) -> {
|
||||
PoolDataSourceImpl ds = context.getBean(PoolDataSourceImpl.class);
|
||||
assertThat(ds.getConnectionPoolName()).isEqualTo("myOracleUcpDS");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesJdbcConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(PoolDataSourceImpl.class);
|
||||
PoolDataSourceImpl oracleUcp = (PoolDataSourceImpl) dataSource;
|
||||
assertThat(oracleUcp.getUser()).isEqualTo("user-1");
|
||||
assertThat(oracleUcp).extracting("password")
|
||||
.extracting((o) -> ((OpaqueString) o).get())
|
||||
.isEqualTo("password-1");
|
||||
assertThat(oracleUcp.getConnectionFactoryClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(oracleUcp.getURL()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 java.sql.SQLException;
|
||||
|
||||
import oracle.ucp.jdbc.PoolDataSourceImpl;
|
||||
import oracle.ucp.util.OpaqueString;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OracleUcpJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OracleUcpJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() throws SQLException {
|
||||
PoolDataSourceImpl dataSource = new PoolDataSourceImpl();
|
||||
dataSource.setURL("will-be-overwritten");
|
||||
dataSource.setUser("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setConnectionFactoryClassName("will-be-overwritten");
|
||||
new OracleUcpJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getURL()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUser()).isEqualTo("user-1");
|
||||
assertThat(dataSource).extracting("password")
|
||||
.extracting((password) -> ((OpaqueString) password).get())
|
||||
.isEqualTo("password-1");
|
||||
assertThat(dataSource.getConnectionFactoryClassName())
|
||||
.isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* {@link JdbcConnectionDetails} used in tests.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class TestJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://postgres.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
}
|
@ -24,9 +24,11 @@ import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -41,6 +43,9 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatDataSourceConfigurationTests {
|
||||
|
||||
@ -48,6 +53,10 @@ class TomcatDataSourceConfigurationTests {
|
||||
|
||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.type=" + org.apache.tomcat.jdbc.pool.DataSource.class.getName());
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
TestPropertyValues.of(PREFIX + "initialize:false").applyTo(this.context);
|
||||
@ -106,6 +115,32 @@ class TomcatDataSourceConfigurationTests {
|
||||
assertThat(ds.getValidationInterval()).isEqualTo(3000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesJdbcConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class)
|
||||
.withPropertyValues(PREFIX + "url=jdbc:broken", PREFIX + "username=alice", PREFIX + "password=secret")
|
||||
.run((context) -> {
|
||||
DataSource dataSource = context.getBean(DataSource.class);
|
||||
assertThat(dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
org.apache.tomcat.jdbc.pool.DataSource tomcat = (org.apache.tomcat.jdbc.pool.DataSource) dataSource;
|
||||
assertThat(tomcat.getPoolProperties().getUsername()).isEqualTo("user-1");
|
||||
assertThat(tomcat.getPoolProperties().getPassword()).isEqualTo("password-1");
|
||||
assertThat(tomcat.getPoolProperties().getDriverClassName()).isEqualTo("org.postgresql.Driver");
|
||||
assertThat(tomcat.getPoolProperties().getUrl())
|
||||
.isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@EnableMBeanExport
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 org.apache.tomcat.jdbc.pool.DataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TomcatJdbcConnectionDetailsBeanPostProcessor}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TomcatJdbcConnectionDetailsBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void setUsernamePasswordAndUrl() {
|
||||
DataSource dataSource = new DataSource();
|
||||
dataSource.setUrl("will-be-overwritten");
|
||||
dataSource.setUsername("will-be-overwritten");
|
||||
dataSource.setPassword("will-be-overwritten");
|
||||
dataSource.setDriverClassName("will-be-overwritten");
|
||||
new TomcatJdbcConnectionDetailsBeanPostProcessor(null).processDataSource(dataSource,
|
||||
new TestJdbcConnectionDetails());
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPoolProperties().getPassword()).isEqualTo("password-1");
|
||||
assertThat(dataSource.getPoolProperties().getDriverClassName())
|
||||
.isEqualTo(DatabaseDriver.POSTGRESQL.getDriverClassName());
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,7 @@ import javax.sql.XADataSource;
|
||||
import com.ibm.db2.jcc.DB2XADataSource;
|
||||
import org.hsqldb.jdbc.pool.JDBCXADataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.postgresql.xa.PGXADataSource;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.jdbc.XADataSourceWrapper;
|
||||
@ -40,6 +41,8 @@ import static org.mockito.Mockito.mock;
|
||||
* Tests for {@link XADataSourceAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class XADataSourceAutoConfigurationTests {
|
||||
|
||||
@ -90,6 +93,20 @@ class XADataSourceAutoConfigurationTests {
|
||||
assertThat(dataSource.getLoginTimeout()).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseConnectionDetailsIfAvailable() {
|
||||
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(XADataSourceAutoConfiguration.class))
|
||||
.withUserConfiguration(FromProperties.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run((context) -> {
|
||||
MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class);
|
||||
PGXADataSource dataSource = (PGXADataSource) wrapper.getXaDataSource();
|
||||
assertThat(dataSource).isNotNull();
|
||||
assertThat(dataSource.getUrl()).startsWith("jdbc:postgresql://postgres.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUser()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("password-1");
|
||||
});
|
||||
}
|
||||
|
||||
private ApplicationContext createContext(Class<?> configuration, String... env) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of(env).applyTo(context);
|
||||
@ -98,6 +115,16 @@ class XADataSourceAutoConfigurationTests {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new TestJdbcConnectionDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class WrapExisting {
|
||||
|
||||
|
@ -28,6 +28,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -39,6 +40,7 @@ import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseAutoConfigurationRuntimeHints;
|
||||
@ -75,6 +77,8 @@ import static org.assertj.core.api.Assertions.contentOf;
|
||||
* @author Andrii Hrytsiuk
|
||||
* @author Ferenc Gratzer
|
||||
* @author Evgeniy Cheban
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class LiquibaseAutoConfigurationTests {
|
||||
@ -97,6 +101,18 @@ class LiquibaseAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsDataSourceWithNoDataSourceBeanAndJdbcConnectionDetails() {
|
||||
this.contextRunner.withSystemProperties("shouldRun=false")
|
||||
.withUserConfiguration(JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("secret-1");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWithLiquibaseUrlAndNoSpringJdbc() {
|
||||
this.contextRunner.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:" + UUID.randomUUID())
|
||||
@ -116,6 +132,30 @@ class LiquibaseAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdbcConnectionDetailsAreUsedIfAvailable() {
|
||||
this.contextRunner.withSystemProperties("shouldRun=false")
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
SimpleDriverDataSource dataSource = (SimpleDriverDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getUrl()).isEqualTo("jdbc:postgresql://database.example.com:12345/database-1");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("user-1");
|
||||
assertThat(dataSource.getPassword()).isEqualTo("secret-1");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void liquibaseDataSourceIsUsedOverJdbcConnectionDetails() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(LiquibaseDataSourceConfiguration.class, JdbcConnectionDetailsConfiguration.class)
|
||||
.run(assertLiquibase((liquibase) -> {
|
||||
HikariDataSource dataSource = (HikariDataSource) liquibase.getDataSource();
|
||||
assertThat(dataSource.getJdbcUrl()).startsWith("jdbc:hsqldb:mem:liquibasetest");
|
||||
assertThat(dataSource.getUsername()).isEqualTo("sa");
|
||||
assertThat(dataSource.getPassword()).isNull();
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void changelogXml() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
@ -509,6 +549,33 @@ class LiquibaseAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JdbcConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
JdbcConnectionDetails jdbcConnectionDetails() {
|
||||
return new JdbcConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:postgresql://database.example.com:12345/database-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomH2Driver extends org.h2.Driver {
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user