Integrate @ConfigurationProperties @Beans with DataSource configuration
We now have a much simpler DataSourceAutoConfiguration that binds to whatever DataSource concrete type it finds at runtime. To be able to quickly switch between Hikari and the other types of DataSource there's a minute shim for translating the common properties (username, password, url, driverClassName), but actually only url is different. The shim and also DataSource initialization is supported through DataSourceProperties, but the other native properties get bound directly through the concrete runtime type of the DataSource. The /configprops endpoint works (and is exposed in the actuator sample). Fixes gh-840, fixes gh-477, see also gh-808.
This commit is contained in:
@@ -34,11 +34,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.jdbc.CommonsDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.HikariDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.TomcatDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
|
||||
@@ -54,11 +50,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
|
||||
EmbeddedDataSourceConfiguration.class, CommonsDataSourceConfiguration.class,
|
||||
HikariDataSourceConfiguration.class, TomcatDataSourceConfiguration.class,
|
||||
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class, RabbitAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MongoAutoConfiguration.class,
|
||||
MongoDataAutoConfiguration.class, RedisAutoConfiguration.class,
|
||||
RabbitAutoConfiguration.class })
|
||||
public class HealthIndicatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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
|
||||
*
|
||||
* http://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.actuate.endpoint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ConfigurationPropertiesReportEndpointSerializationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNaming() throws Exception {
|
||||
this.context.register(Config.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "foo.name:foo");
|
||||
this.context.refresh();
|
||||
ConfigurationPropertiesReportEndpoint report = this.context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
Map<String, Object> properties = report.invoke();
|
||||
Map<String, Object> nestedProperties = (Map<String, Object>) properties
|
||||
.get("foo");
|
||||
assertNotNull(nestedProperties);
|
||||
assertEquals("foo", nestedProperties.get("prefix"));
|
||||
Map<String, Object> map = (Map<String, Object>) nestedProperties
|
||||
.get("properties");
|
||||
assertNotNull(map);
|
||||
assertEquals(1, map.size());
|
||||
assertEquals("foo", map.get("name"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public ConfigurationPropertiesReportEndpoint endpoint(
|
||||
ConfigurationBeanFactoryMetaData beanFactoryMetaData) {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = new ConfigurationPropertiesReportEndpoint();
|
||||
endpoint.setConfigurationBeanFactoryMetaData(beanFactoryMetaData);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "foo")
|
||||
public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Foo {
|
||||
|
||||
private String name = "654321";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// No setter so it doesn't appear in the report
|
||||
public String getSummary() {
|
||||
return "Name: " + this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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
|
||||
*
|
||||
* http://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 javax.annotation.PreDestroy;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp.BasicDataSource;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
|
||||
/**
|
||||
* Configuration for a Commons DBCP database pool. The DBCP pool is popular but not
|
||||
* recommended in high volume environments (the Tomcat DataSource is more reliable).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see DataSourceAutoConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonsDataSourceConfiguration extends AbstractDataSourceConfiguration {
|
||||
|
||||
private static Log logger = LogFactory.getLog(CommonsDataSourceConfiguration.class);
|
||||
|
||||
private BasicDataSource pool;
|
||||
|
||||
public CommonsDataSourceConfiguration() {
|
||||
// Ensure to set the correct default value for Commons DBCP
|
||||
setInitialSize(0);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "close")
|
||||
public DataSource dataSource() {
|
||||
logger.info("Hint: using Commons DBCP BasicDataSource. It's going to work, "
|
||||
+ "but the Tomcat DataSource is more reliable.");
|
||||
this.pool = createAndConfigurePool();
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
private BasicDataSource createAndConfigurePool() {
|
||||
BasicDataSource pool = new BasicDataSource();
|
||||
pool.setDriverClassName(getDriverClassName());
|
||||
pool.setUrl(getUrl());
|
||||
if (getUsername() != null) {
|
||||
pool.setUsername(getUsername());
|
||||
}
|
||||
if (getPassword() != null) {
|
||||
pool.setPassword(getPassword());
|
||||
}
|
||||
pool.setInitialSize(getInitialSize());
|
||||
pool.setMaxActive(getMaxActive());
|
||||
pool.setMaxIdle(getMaxIdle());
|
||||
pool.setMinIdle(getMinIdle());
|
||||
pool.setTestOnBorrow(isTestOnBorrow());
|
||||
pool.setTestOnReturn(isTestOnReturn());
|
||||
pool.setTestWhileIdle(isTestWhileIdle());
|
||||
pool.setValidationQuery(getValidationQuery());
|
||||
if (getTimeBetweenEvictionRunsMillis() != null) {
|
||||
pool.setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis());
|
||||
}
|
||||
if (getMinEvictableIdleTimeMillis() != null) {
|
||||
pool.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
|
||||
}
|
||||
if (getMaxWaitMillis() != null) {
|
||||
pool.setMaxWait(getMaxWaitMillis());
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.pool != null) {
|
||||
try {
|
||||
this.pool.close();
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Could not close data source", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,16 +35,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@@ -54,7 +53,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -65,7 +63,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(EmbeddedDatabaseType.class)
|
||||
public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
@EnableConfigurationProperties(DataSourceProperties.class)
|
||||
public class DataSourceAutoConfiguration {
|
||||
|
||||
private static Log logger = LogFactory.getLog(DataSourceAutoConfiguration.class);
|
||||
|
||||
@@ -77,26 +76,20 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private RelaxedPropertyResolver datasourceProperties;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.datasourceProperties = new RelaxedPropertyResolver(environment,
|
||||
CONFIGURATION_PREFIX + ".");
|
||||
}
|
||||
@Autowired
|
||||
private DataSourceProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() throws Exception {
|
||||
boolean initialize = this.datasourceProperties.getProperty("initialize",
|
||||
Boolean.class, true);
|
||||
boolean initialize = this.properties.isInitialize();
|
||||
if (this.dataSource == null || !initialize) {
|
||||
logger.debug("No DataSource found so not initializing");
|
||||
return;
|
||||
}
|
||||
|
||||
String schema = this.datasourceProperties.getProperty("schema");
|
||||
String schema = this.properties.getSchema();
|
||||
if (schema == null) {
|
||||
String platform = this.datasourceProperties.getProperty("platform", "all");
|
||||
String platform = this.properties.getPlatform();
|
||||
schema = "classpath*:schema-" + platform + ".sql,";
|
||||
schema += "classpath*:schema.sql,";
|
||||
schema += "classpath*:data-" + platform + ".sql,";
|
||||
@@ -109,8 +102,7 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
.getResources(schemaLocation)));
|
||||
}
|
||||
|
||||
boolean continueOnError = this.datasourceProperties.getProperty(
|
||||
"continueOnError", Boolean.class, false);
|
||||
boolean continueOnError = this.properties.isContinueOnError();
|
||||
boolean exists = false;
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
for (Resource resource : resources) {
|
||||
@@ -120,7 +112,7 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
populator.setContinueOnError(continueOnError);
|
||||
}
|
||||
}
|
||||
populator.setSeparator(this.datasourceProperties.getProperty("separator", ";"));
|
||||
populator.setSeparator(this.properties.getSeparator());
|
||||
|
||||
if (exists) {
|
||||
DatabasePopulatorUtils.execute(populator, this.dataSource);
|
||||
@@ -151,24 +143,26 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
|
||||
}
|
||||
|
||||
@Conditional(DataSourceAutoConfiguration.TomcatDatabaseCondition.class)
|
||||
@Conditional(DataSourceAutoConfiguration.NonEmbeddedDatabaseCondition.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@Import(TomcatDataSourceConfiguration.class)
|
||||
protected static class TomcatConfiguration {
|
||||
protected static class NonEmbeddedConfiguration {
|
||||
|
||||
}
|
||||
@Autowired
|
||||
private DataSourceProperties properties;
|
||||
|
||||
@Conditional(DataSourceAutoConfiguration.HikariDatabaseCondition.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@Import(HikariDataSourceConfiguration.class)
|
||||
protected static class HikariConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Conditional(DataSourceAutoConfiguration.BasicDatabaseCondition.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@Import(CommonsDataSourceConfiguration.class)
|
||||
protected static class DbcpConfiguration {
|
||||
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
// @formatter:off
|
||||
DataSourceFactory factory = DataSourceFactory
|
||||
.create(this.properties.getClassLoader())
|
||||
.driverClassName(this.properties.getDriverClassName())
|
||||
.url(this.properties.getUrl())
|
||||
.username(this.properties.getUsername())
|
||||
.password(this.properties.getPassword());
|
||||
// @formatter:on
|
||||
return factory.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -196,35 +190,18 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
/**
|
||||
* Base {@link Condition} for non-embedded database checks.
|
||||
*/
|
||||
static abstract class NonEmbeddedDatabaseCondition extends SpringBootCondition {
|
||||
|
||||
protected abstract String getDataSourceClassName();
|
||||
static class NonEmbeddedDatabaseCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
|
||||
if (!ClassUtils.isPresent(getDataSourceClassName(), context.getClassLoader())) {
|
||||
return ConditionOutcome.noMatch(getDataSourceClassName()
|
||||
+ " DataSource class not found");
|
||||
ClassLoader dataSourceClassLoader = getDataSourceClassLoader(context);
|
||||
if (dataSourceClassLoader != null) {
|
||||
return ConditionOutcome.match("Supported DataSource class found");
|
||||
}
|
||||
|
||||
String driverClassName = getDriverClassName(context.getEnvironment(),
|
||||
getDataSourceClassLoader(context));
|
||||
if (driverClassName == null) {
|
||||
return ConditionOutcome.noMatch("no database driver");
|
||||
}
|
||||
|
||||
String url = getUrl(context.getEnvironment(), context.getClassLoader());
|
||||
if (url == null) {
|
||||
return ConditionOutcome.noMatch("no database URL");
|
||||
}
|
||||
|
||||
if (ClassUtils.isPresent(driverClassName, context.getClassLoader())) {
|
||||
return ConditionOutcome.match("found database driver " + driverClassName);
|
||||
}
|
||||
|
||||
return ConditionOutcome.noMatch("missing database driver " + driverClassName);
|
||||
return ConditionOutcome.noMatch("missing supported DataSource");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,94 +209,12 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
* the driver class can actually be loaded by the data source.
|
||||
*/
|
||||
private ClassLoader getDataSourceClassLoader(ConditionContext context) {
|
||||
try {
|
||||
Class<?> dataSourceClass = ClassUtils.forName(getDataSourceClassName(),
|
||||
context.getClassLoader());
|
||||
return dataSourceClass.getClassLoader();
|
||||
Class<?> dataSourceClass = new DataSourceFactory(context.getClassLoader())
|
||||
.findType();
|
||||
if (dataSourceClass == null) {
|
||||
return null;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDriverClassName(Environment environment, ClassLoader classLoader) {
|
||||
String driverClassName = environment == null ? null : environment
|
||||
.getProperty(CONFIGURATION_PREFIX + ".driverClassName");
|
||||
if (driverClassName == null) {
|
||||
driverClassName = EmbeddedDatabaseConnection.get(classLoader)
|
||||
.getDriverClassName();
|
||||
}
|
||||
return driverClassName;
|
||||
}
|
||||
|
||||
private String getUrl(Environment environment, ClassLoader classLoader) {
|
||||
String url = (environment == null ? null : environment
|
||||
.getProperty(CONFIGURATION_PREFIX + ".url"));
|
||||
if (url == null) {
|
||||
url = EmbeddedDatabaseConnection.get(classLoader).getUrl();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Condition} to detect when a commons-dbcp {@code BasicDataSource} backed
|
||||
* database is used.
|
||||
*/
|
||||
static class BasicDatabaseCondition extends NonEmbeddedDatabaseCondition {
|
||||
|
||||
private final Condition hikariCondition = new HikariDatabaseCondition();
|
||||
|
||||
private final Condition tomcatCondition = new TomcatDatabaseCondition();
|
||||
|
||||
@Override
|
||||
protected String getDataSourceClassName() {
|
||||
return "org.apache.commons.dbcp.BasicDataSource";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (anyMatches(context, metadata, this.hikariCondition, this.tomcatCondition)) {
|
||||
return ConditionOutcome.noMatch("other DataSource");
|
||||
}
|
||||
return super.getMatchOutcome(context, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Condition} to detect when a Hikari DataSource backed database is used.
|
||||
*/
|
||||
static class HikariDatabaseCondition extends NonEmbeddedDatabaseCondition {
|
||||
|
||||
private final Condition tomcatCondition = new TomcatDatabaseCondition();
|
||||
|
||||
@Override
|
||||
protected String getDataSourceClassName() {
|
||||
return "com.zaxxer.hikari.HikariDataSource";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (anyMatches(context, metadata, this.tomcatCondition)) {
|
||||
return ConditionOutcome.noMatch("Tomcat DataSource");
|
||||
}
|
||||
return super.getMatchOutcome(context, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Condition} to detect when a Tomcat DataSource backed database is used.
|
||||
*/
|
||||
static class TomcatDatabaseCondition extends NonEmbeddedDatabaseCondition {
|
||||
|
||||
@Override
|
||||
protected String getDataSourceClassName() {
|
||||
return "org.apache.tomcat.jdbc.pool.DataSource";
|
||||
return dataSourceClass.getClassLoader();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -329,17 +224,12 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
*/
|
||||
static class EmbeddedDatabaseCondition extends SpringBootCondition {
|
||||
|
||||
private final SpringBootCondition hikariCondition = new HikariDatabaseCondition();
|
||||
|
||||
private final SpringBootCondition tomcatCondition = new TomcatDatabaseCondition();
|
||||
|
||||
private final SpringBootCondition dbcpCondition = new BasicDatabaseCondition();
|
||||
private final SpringBootCondition nonEmbedded = new NonEmbeddedDatabaseCondition();
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (anyMatches(context, metadata, this.hikariCondition, this.tomcatCondition,
|
||||
this.dbcpCondition)) {
|
||||
if (anyMatches(context, metadata, this.nonEmbedded)) {
|
||||
return ConditionOutcome
|
||||
.noMatch("existing non-embedded database detected");
|
||||
}
|
||||
@@ -358,11 +248,7 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
*/
|
||||
static class DatabaseCondition extends SpringBootCondition {
|
||||
|
||||
private final SpringBootCondition hikariCondition = new HikariDatabaseCondition();
|
||||
|
||||
private final SpringBootCondition tomcatCondition = new TomcatDatabaseCondition();
|
||||
|
||||
private final SpringBootCondition dbcpCondition = new BasicDatabaseCondition();
|
||||
private final SpringBootCondition nonEmbedded = new NonEmbeddedDatabaseCondition();
|
||||
|
||||
private final SpringBootCondition embeddedCondition = new EmbeddedDatabaseCondition();
|
||||
|
||||
@@ -370,8 +256,7 @@ public class DataSourceAutoConfiguration implements EnvironmentAware {
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
|
||||
if (anyMatches(context, metadata, this.hikariCondition, this.tomcatCondition,
|
||||
this.dbcpCondition, this.embeddedCondition)) {
|
||||
if (anyMatches(context, metadata, this.nonEmbedded, this.embeddedCondition)) {
|
||||
return ConditionOutcome.match("existing auto database detected");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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
|
||||
*
|
||||
* http://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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Convenience class for building a {@link DataSource} with common implementations and
|
||||
* properties. If Tomcat, HikariCP or Commons DBCP are on the classpath one of them will
|
||||
* be selected (in that order with Tomcat 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</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DataSourceFactory {
|
||||
|
||||
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
|
||||
"org.apache.tomcat.jdbc.pool.DataSource",
|
||||
"com.zaxxer.hikari.HikariDataSource",
|
||||
"org.apache.commons.dbcp.BasicDataSource" };
|
||||
|
||||
private Class<? extends DataSource> type;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
|
||||
public static DataSourceFactory create() {
|
||||
return new DataSourceFactory(null);
|
||||
}
|
||||
|
||||
public static DataSourceFactory create(ClassLoader classLoader) {
|
||||
return new DataSourceFactory(classLoader);
|
||||
}
|
||||
|
||||
public DataSourceFactory(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public DataSource build() {
|
||||
Class<? extends DataSource> type = getType();
|
||||
DataSource result = BeanUtils.instantiate(type);
|
||||
bind(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void bind(DataSource result) {
|
||||
new RelaxedDataBinder(result).bind(getPropertyValues());
|
||||
}
|
||||
|
||||
private PropertyValues getPropertyValues() {
|
||||
if (getType().getName().contains("Hikari") && this.properties.containsKey("url")) {
|
||||
this.properties.put("jdbcUrl", this.properties.get("url"));
|
||||
this.properties.remove("url");
|
||||
}
|
||||
return new MutablePropertyValues(this.properties);
|
||||
}
|
||||
|
||||
public DataSourceFactory type(Class<? extends DataSource> type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataSourceFactory url(String url) {
|
||||
this.properties.put("url", url);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataSourceFactory driverClassName(String driverClassName) {
|
||||
this.properties.put("driverClassName", driverClassName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataSourceFactory username(String username) {
|
||||
this.properties.put("username", username);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataSourceFactory password(String password) {
|
||||
this.properties.put("password", password);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Class<? extends DataSource> findType() {
|
||||
if (this.type != null) {
|
||||
return this.type;
|
||||
}
|
||||
for (String name : DATA_SOURCE_TYPE_NAMES) {
|
||||
if (ClassUtils.isPresent(name, this.classLoader)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<DataSource> resolved = (Class<DataSource>) ClassUtils
|
||||
.resolveClassName(name, this.classLoader);
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<? extends DataSource> getType() {
|
||||
Class<? extends DataSource> type = findType();
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
throw new IllegalStateException("No supported DataSource type found");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -29,9 +28,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
|
||||
@EnableConfigurationProperties
|
||||
public abstract class AbstractDataSourceConfiguration implements BeanClassLoaderAware,
|
||||
InitializingBean {
|
||||
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
|
||||
|
||||
private String driverClassName;
|
||||
|
||||
@@ -41,30 +38,18 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
|
||||
private String password;
|
||||
|
||||
private int maxActive = 100;
|
||||
|
||||
private int maxIdle = 8;
|
||||
|
||||
private int minIdle = 8;
|
||||
|
||||
private int initialSize = 10;
|
||||
|
||||
private String validationQuery;
|
||||
|
||||
private boolean testOnBorrow;
|
||||
|
||||
private boolean testOnReturn;
|
||||
|
||||
private boolean testWhileIdle;
|
||||
|
||||
private Integer timeBetweenEvictionRunsMillis;
|
||||
|
||||
private Integer minEvictableIdleTimeMillis;
|
||||
|
||||
private Integer maxWaitMillis;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private boolean initialize = true;
|
||||
|
||||
private String platform = "all";
|
||||
|
||||
private String schema;
|
||||
|
||||
private boolean continueOnError = false;
|
||||
|
||||
private String separator = ";";
|
||||
|
||||
private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
|
||||
|
||||
@Override
|
||||
@@ -78,7 +63,7 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
.get(this.classLoader);
|
||||
}
|
||||
|
||||
protected String getDriverClassName() {
|
||||
public String getDriverClassName() {
|
||||
if (StringUtils.hasText(this.driverClassName)) {
|
||||
return this.driverClassName;
|
||||
}
|
||||
@@ -93,7 +78,7 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
return driverClassName;
|
||||
}
|
||||
|
||||
protected String getUrl() {
|
||||
public String getUrl() {
|
||||
if (StringUtils.hasText(this.url)) {
|
||||
return this.url;
|
||||
}
|
||||
@@ -108,7 +93,7 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
return url;
|
||||
}
|
||||
|
||||
protected String getUsername() {
|
||||
public String getUsername() {
|
||||
if (StringUtils.hasText(this.username)) {
|
||||
return this.username;
|
||||
}
|
||||
@@ -118,7 +103,7 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getPassword() {
|
||||
public String getPassword() {
|
||||
if (StringUtils.hasText(this.password)) {
|
||||
return this.password;
|
||||
}
|
||||
@@ -132,10 +117,6 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
this.driverClassName = driverClassName;
|
||||
}
|
||||
|
||||
public void setInitialSize(int initialSize) {
|
||||
this.initialSize = initialSize;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
@@ -148,88 +129,48 @@ public abstract class AbstractDataSourceConfiguration implements BeanClassLoader
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setMaxActive(int maxActive) {
|
||||
this.maxActive = maxActive;
|
||||
public boolean isInitialize() {
|
||||
return this.initialize;
|
||||
}
|
||||
|
||||
public void setMaxIdle(int maxIdle) {
|
||||
this.maxIdle = maxIdle;
|
||||
public void setInitialize(boolean initialize) {
|
||||
this.initialize = initialize;
|
||||
}
|
||||
|
||||
public void setMinIdle(int minIdle) {
|
||||
this.minIdle = minIdle;
|
||||
public String getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
public void setValidationQuery(String validationQuery) {
|
||||
this.validationQuery = validationQuery;
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public void setTestOnBorrow(boolean testOnBorrow) {
|
||||
this.testOnBorrow = testOnBorrow;
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public void setTestOnReturn(boolean testOnReturn) {
|
||||
this.testOnReturn = testOnReturn;
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public void setTestWhileIdle(boolean testWhileIdle) {
|
||||
this.testWhileIdle = testWhileIdle;
|
||||
public boolean isContinueOnError() {
|
||||
return this.continueOnError;
|
||||
}
|
||||
|
||||
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
|
||||
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
|
||||
public void setContinueOnError(boolean continueOnError) {
|
||||
this.continueOnError = continueOnError;
|
||||
}
|
||||
|
||||
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
|
||||
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
|
||||
public String getSeparator() {
|
||||
return this.separator;
|
||||
}
|
||||
|
||||
public void setMaxWait(int maxWaitMillis) {
|
||||
this.maxWaitMillis = maxWaitMillis;
|
||||
public void setSeparator(String separator) {
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
public int getInitialSize() {
|
||||
return this.initialSize;
|
||||
}
|
||||
|
||||
protected int getMaxActive() {
|
||||
return this.maxActive;
|
||||
}
|
||||
|
||||
protected int getMaxIdle() {
|
||||
return this.maxIdle;
|
||||
}
|
||||
|
||||
protected int getMinIdle() {
|
||||
return this.minIdle;
|
||||
}
|
||||
|
||||
protected String getValidationQuery() {
|
||||
return this.validationQuery;
|
||||
}
|
||||
|
||||
protected boolean isTestOnBorrow() {
|
||||
return this.testOnBorrow;
|
||||
}
|
||||
|
||||
protected boolean isTestOnReturn() {
|
||||
return this.testOnReturn;
|
||||
}
|
||||
|
||||
protected boolean isTestWhileIdle() {
|
||||
return this.testWhileIdle;
|
||||
}
|
||||
|
||||
protected Integer getTimeBetweenEvictionRunsMillis() {
|
||||
return this.timeBetweenEvictionRunsMillis;
|
||||
}
|
||||
|
||||
protected Integer getMinEvictableIdleTimeMillis() {
|
||||
return this.minEvictableIdleTimeMillis;
|
||||
}
|
||||
|
||||
protected Integer getMaxWaitMillis() {
|
||||
return this.maxWaitMillis;
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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
|
||||
*
|
||||
* http://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.util.Properties;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
/**
|
||||
* Configuration for a HikariCP database pool. The HikariCP pool is a popular data source
|
||||
* implementation that provides high performance as well as some useful opinionated
|
||||
* defaults. For compatibility with other DataSource implementations accepts configuration
|
||||
* via properties in "spring.datasource.*", e.g. "url", "driverClassName", "username",
|
||||
* "password" (and some others but the full list supported by the Tomcat pool is not
|
||||
* applicable). Note that the Hikari team recommends using a "dataSourceClassName" and a
|
||||
* Properties instance (specified here as "spring.datasource.hikari.*"). This makes the
|
||||
* binding potentially vendor specific, but gives you full control of all the native
|
||||
* features in the vendor's DataSource.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see DataSourceAutoConfiguration
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class HikariDataSourceConfiguration extends AbstractDataSourceConfiguration {
|
||||
|
||||
private String dataSourceClassName;
|
||||
|
||||
private String username;
|
||||
|
||||
private HikariDataSource pool;
|
||||
|
||||
private Properties hikari = new Properties();
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
public DataSource dataSource() {
|
||||
this.pool = new HikariDataSource();
|
||||
if (this.dataSourceClassName == null) {
|
||||
this.pool.setDriverClassName(getDriverClassName());
|
||||
}
|
||||
else {
|
||||
this.pool.setDataSourceClassName(this.dataSourceClassName);
|
||||
this.pool.setDataSourceProperties(this.hikari);
|
||||
}
|
||||
this.pool.setJdbcUrl(getUrl());
|
||||
if (getUsername() != null) {
|
||||
this.pool.setUsername(getUsername());
|
||||
}
|
||||
if (getPassword() != null) {
|
||||
this.pool.setPassword(getPassword());
|
||||
}
|
||||
this.pool.setMaximumPoolSize(getMaxActive());
|
||||
this.pool.setMinimumIdle(getMinIdle());
|
||||
if (isTestOnBorrow()) {
|
||||
this.pool.setConnectionInitSql(getValidationQuery());
|
||||
}
|
||||
else {
|
||||
this.pool.setConnectionTestQuery(getValidationQuery());
|
||||
}
|
||||
if (getMaxWaitMillis() != null) {
|
||||
this.pool.setMaxLifetime(getMaxWaitMillis());
|
||||
}
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.pool != null) {
|
||||
this.pool.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataSourceClassName the dataSourceClassName to set
|
||||
*/
|
||||
public void setDataSourceClassName(String dataSourceClassName) {
|
||||
this.dataSourceClassName = dataSourceClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hikari data source properties
|
||||
*/
|
||||
public Properties getHikari() {
|
||||
return this.hikari;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUsername() {
|
||||
if (StringUtils.hasText(this.username)) {
|
||||
return this.username;
|
||||
}
|
||||
if (this.dataSourceClassName == null
|
||||
&& EmbeddedDatabaseConnection.isEmbedded(getDriverClassName())) {
|
||||
return "sa";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.bind.PropertySourcesPropertyValues;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class RelaxedDataSourceFactory {
|
||||
|
||||
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
|
||||
"com.zaxxer.hikari.HikariDataSource",
|
||||
"org.apache.tomcat.jdbc.pool.DataSource",
|
||||
"org.apache.commons.dbcp.BasicDataSource" };
|
||||
|
||||
private Class<? extends DataSource> type;
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
public static RelaxedDataSourceFactory create(ConfigurableEnvironment environment) {
|
||||
return new RelaxedDataSourceFactory(environment);
|
||||
}
|
||||
|
||||
public RelaxedDataSourceFactory(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public DataSource build(String prefix) {
|
||||
Class<? extends DataSource> type = getType();
|
||||
DataSource result = BeanUtils.instantiate(type);
|
||||
RelaxedDataBinder binder = new RelaxedDataBinder(result, prefix);
|
||||
binder.bind(new PropertySourcesPropertyValues(this.environment
|
||||
.getPropertySources()));
|
||||
return result;
|
||||
}
|
||||
|
||||
public RelaxedDataSourceFactory type(Class<? extends DataSource> type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Class<? extends DataSource> getType() {
|
||||
if (this.type != null) {
|
||||
return this.type;
|
||||
}
|
||||
for (String name : DATA_SOURCE_TYPE_NAMES) {
|
||||
if (ClassUtils.isPresent(name, null)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<DataSource> resolved = (Class<DataSource>) ClassUtils
|
||||
.resolveClassName(name, null);
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No supported DataSource type found");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for a Tomcat database pool. The Tomcat pool provides superior performance
|
||||
* and tends not to deadlock in high volume environments.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see DataSourceAutoConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class TomcatDataSourceConfiguration extends AbstractDataSourceConfiguration {
|
||||
|
||||
private String jdbcInterceptors;
|
||||
private long validationInterval = 30000;
|
||||
private org.apache.tomcat.jdbc.pool.DataSource pool;
|
||||
|
||||
@Bean(destroyMethod = "close")
|
||||
public DataSource dataSource() {
|
||||
this.pool = new org.apache.tomcat.jdbc.pool.DataSource();
|
||||
this.pool.setDriverClassName(getDriverClassName());
|
||||
this.pool.setUrl(getUrl());
|
||||
if (getUsername() != null) {
|
||||
this.pool.setUsername(getUsername());
|
||||
}
|
||||
if (getPassword() != null) {
|
||||
this.pool.setPassword(getPassword());
|
||||
}
|
||||
this.pool.setInitialSize(getInitialSize());
|
||||
this.pool.setMaxActive(getMaxActive());
|
||||
this.pool.setMaxIdle(getMaxIdle());
|
||||
this.pool.setMinIdle(getMinIdle());
|
||||
this.pool.setTestOnBorrow(isTestOnBorrow());
|
||||
this.pool.setTestOnReturn(isTestOnReturn());
|
||||
this.pool.setTestWhileIdle(isTestWhileIdle());
|
||||
if (getTimeBetweenEvictionRunsMillis() != null) {
|
||||
this.pool
|
||||
.setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis());
|
||||
}
|
||||
if (getMinEvictableIdleTimeMillis() != null) {
|
||||
this.pool.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
|
||||
}
|
||||
this.pool.setValidationQuery(getValidationQuery());
|
||||
this.pool.setValidationInterval(this.validationInterval);
|
||||
if (getMaxWaitMillis() != null) {
|
||||
this.pool.setMaxWait(getMaxWaitMillis());
|
||||
}
|
||||
if (this.jdbcInterceptors != null) {
|
||||
this.pool.setJdbcInterceptors(this.jdbcInterceptors);
|
||||
}
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.pool != null) {
|
||||
this.pool.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void setJdbcInterceptors(String jdbcInterceptors) {
|
||||
this.jdbcInterceptors = jdbcInterceptors;
|
||||
}
|
||||
|
||||
public void setValidationInterval(long validationInterval) {
|
||||
this.validationInterval = validationInterval;
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,12 @@ import javax.sql.DataSource;
|
||||
import org.apache.commons.dbcp.BasicDataSource;
|
||||
import org.apache.commons.pool.impl.GenericObjectPool;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -83,4 +87,16 @@ public class CommonsDataSourceConfigurationTests {
|
||||
assertEquals(GenericObjectPool.DEFAULT_MAX_WAIT, ds.getMaxWait());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
protected static class CommonsDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
|
||||
public DataSource dataSource() {
|
||||
return DataSourceFactory.create().type(BasicDataSource.class).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import java.sql.Driver;
|
||||
import java.sql.DriverPropertyInfo;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
@@ -32,15 +30,16 @@ import java.util.logging.Logger;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp.BasicDataSource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
@@ -64,11 +63,17 @@ public class DataSourceAutoConfigurationTests {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
EmbeddedDatabaseConnection.override = null;
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.initialize:false",
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());
|
||||
}
|
||||
|
||||
@After
|
||||
public void restore() {
|
||||
EmbeddedDatabaseConnection.override = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultDataSourceExists() throws Exception {
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
@@ -78,7 +83,36 @@ public class DataSourceAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTomcatIsFallback() throws Exception {
|
||||
public void testDataSourceUrlHasEmbeddedDefault() throws Exception {
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
org.apache.tomcat.jdbc.pool.DataSource dataSource = this.context
|
||||
.getBean(org.apache.tomcat.jdbc.pool.DataSource.class);
|
||||
assertNotNull(dataSource.getUrl());
|
||||
assertNotNull(dataSource.getDriverClassName());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadUrl() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadDriverClass() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHikariIsFallback() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.driverClassName:org.hsqldb.jdbcDriver",
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:testdb");
|
||||
@@ -194,11 +228,12 @@ public class DataSourceAutoConfigurationTests {
|
||||
public void testDataSourceInitializedWithExplicitScript() throws Exception {
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(DataSourceAutoConfiguration.CONFIGURATION_PREFIX + ".schema",
|
||||
ClassUtils.addResourcePathToPackagePath(getClass(), "schema.sql"));
|
||||
this.context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test", map));
|
||||
EnvironmentTestUtils.addEnvironment(
|
||||
this.context,
|
||||
"spring.datasource.initialize:true",
|
||||
"spring.datasource.schema:"
|
||||
+ ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"schema.sql"));
|
||||
this.context.refresh();
|
||||
DataSource dataSource = this.context.getBean(DataSource.class);
|
||||
assertTrue(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource);
|
||||
|
||||
@@ -22,10 +22,12 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
@@ -61,8 +63,8 @@ public class HikariDataSourceConfigurationTests {
|
||||
public void testDataSourcePropertiesOverridden() throws Exception {
|
||||
this.context.register(HikariDataSourceConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX
|
||||
+ "url:jdbc:foo//bar/spam");
|
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "maxWait:1234");
|
||||
+ "jdbcUrl:jdbc:foo//bar/spam");
|
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "maxLifetime:1234");
|
||||
this.context.refresh();
|
||||
HikariDataSource ds = this.context.getBean(HikariDataSource.class);
|
||||
assertEquals("jdbc:foo//bar/spam", ds.getJdbcUrl());
|
||||
@@ -74,11 +76,11 @@ public class HikariDataSourceConfigurationTests {
|
||||
public void testDataSourceGenericPropertiesOverridden() throws Exception {
|
||||
this.context.register(HikariDataSourceConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX
|
||||
+ "hikari.databaseName:foo", PREFIX
|
||||
+ "dataSourceClassName:org.h2.JDBCDataSource");
|
||||
+ "dataSourceProperties.dataSourceClassName:org.h2.JDBCDataSource");
|
||||
this.context.refresh();
|
||||
HikariDataSource ds = this.context.getBean(HikariDataSource.class);
|
||||
assertEquals("foo", ds.getDataSourceProperties().getProperty("databaseName"));
|
||||
assertEquals("org.h2.JDBCDataSource",
|
||||
ds.getDataSourceProperties().getProperty("dataSourceClassName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,28 +91,23 @@ public class HikariDataSourceConfigurationTests {
|
||||
assertEquals(1800000, ds.getMaxLifetime());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadUrl() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(HikariDataSourceConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadDriverClass() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(HikariDataSourceConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getField(Class<?> target, String name) {
|
||||
Field field = ReflectionUtils.findField(target, name, null);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return (T) ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
protected static class HikariDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
|
||||
public DataSource dataSource() {
|
||||
return DataSourceFactory.create().type(HikariDataSource.class).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,11 +24,14 @@ import org.apache.tomcat.jdbc.pool.DataSourceProxy;
|
||||
import org.apache.tomcat.jdbc.pool.PoolProperties;
|
||||
import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -47,6 +50,11 @@ public class TomcatDataSourceConfigurationTests {
|
||||
|
||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "initialize:false");
|
||||
}
|
||||
|
||||
@After
|
||||
public void restore() {
|
||||
EmbeddedDatabaseConnection.override = null;
|
||||
@@ -115,28 +123,24 @@ public class TomcatDataSourceConfigurationTests {
|
||||
assertEquals(30000L, ds.getValidationInterval());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadUrl() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(TomcatDataSourceConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadDriverClass() throws Exception {
|
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE;
|
||||
this.context.register(TomcatDataSourceConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(DataSource.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getField(Class<?> target, String name) {
|
||||
Field field = ReflectionUtils.findField(target, name, null);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return (T) ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(DataSourceAutoConfiguration.class)
|
||||
protected static class TomcatDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
|
||||
public DataSource dataSource() {
|
||||
return DataSourceFactory.create()
|
||||
.type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,9 +40,11 @@ dependencies {
|
||||
}
|
||||
|
||||
compile("org.springframework.boot:spring-boot-starter-actuator")
|
||||
compile("org.springframework.boot:spring-boot-starter-jdbc")
|
||||
compile("org.springframework.boot:spring-boot-starter-security")
|
||||
compile("org.springframework.boot:spring-boot-starter-web")
|
||||
|
||||
compile("com.h2database:h2")
|
||||
|
||||
testCompile("org.springframework.boot:spring-boot-starter-test")
|
||||
|
||||
insecure configurations.runtime
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-remote-shell</artifactId>
|
||||
@@ -39,6 +43,10 @@
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for endpoints configuration.
|
||||
@@ -66,7 +67,6 @@ public class EndpointsPropertiesSampleActuatorApplicationTests {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/admin/health", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
String body = entity.getBody();
|
||||
assertEquals("{\"status\":\"ok\"}", body);
|
||||
assertTrue("Wrong body: " + entity.getBody(), entity.getBody().contains("\"status\":\"ok\""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for separate management and main service ports.
|
||||
@@ -71,7 +72,7 @@ public class ManagementAddressActuatorApplicationTests {
|
||||
"http://localhost:" + this.managementPort + "/admin/health",
|
||||
String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertEquals("{\"status\":\"ok\"}", entity.getBody());
|
||||
assertTrue("Wrong body: " + entity.getBody(), entity.getBody().contains("\"status\":\"ok\""));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for separate management and main service ports.
|
||||
@@ -80,7 +81,7 @@ public class ManagementPortSampleActuatorApplicationTests {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.managementPort + "/health", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertEquals("{\"status\":\"ok\"}", entity.getBody());
|
||||
assertTrue("Wrong body: " + entity.getBody(), entity.getBody().contains("\"status\":\"ok\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -130,7 +130,7 @@ public class SampleActuatorApplicationTests {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/health", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertEquals("{\"status\":\"ok\"}", entity.getBody());
|
||||
assertTrue("Wrong body: " + entity.getBody(), entity.getBody().contains("\"status\":\"ok\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user