diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java index 30f776e9d20..51b6e3353a3 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -25,6 +25,7 @@ import java.util.List; import javax.sql.DataSource; import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.IncorrectResultSetColumnCountException; @@ -131,10 +132,8 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator protected String getValidationQuery(String product) { String query = this.query; if (!StringUtils.hasText(query)) { - Product specific = Product.forProduct(product); - if (specific != null) { - query = specific.getQuery(); - } + DatabaseDriver specific = DatabaseDriver.fromProductName(product); + query = specific.getValidationQuery(); } if (!StringUtils.hasText(query)) { query = DEFAULT_QUERY; @@ -185,74 +184,4 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator } - /** - * Known database products. - */ - protected enum Product { - - HSQLDB("HSQL Database Engine", - "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"), - - ORACLE("Oracle", "SELECT 'Hello' from DUAL"), - - DERBY("Apache Derby", "SELECT 1 FROM SYSIBM.SYSDUMMY1"), - - DB2("DB2", "SELECT 1 FROM SYSIBM.SYSDUMMY1") { - - @Override - protected boolean matchesProduct(String product) { - return super.matchesProduct(product) - || product.toLowerCase().startsWith("db2/"); - } - - }, - - DB2_AS400("DB2 UDB for AS/400", "SELECT 1 FROM SYSIBM.SYSDUMMY1") { - @Override - protected boolean matchesProduct(String product) { - return super.matchesProduct(product) - || product.toLowerCase().contains("as/400"); - } - }, - - INFORMIX("Informix Dynamic Server", "select count(*) from systables"), - - FIREBIRD("Firebird", "SELECT 1 FROM RDB$DATABASE") { - - @Override - protected boolean matchesProduct(String product) { - return super.matchesProduct(product) - || product.toLowerCase().startsWith("firebird"); - } - - }; - - private final String product; - - private final String query; - - Product(String product, String query) { - this.product = product; - this.query = query; - } - - protected boolean matchesProduct(String product) { - return this.product.equalsIgnoreCase(product); - } - - public String getQuery() { - return this.query; - } - - public static Product forProduct(String product) { - for (Product candidate : values()) { - if (candidate.matchesProduct(product)) { - return candidate; - } - } - return null; - } - - } - } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/DataSourceHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/DataSourceHealthIndicatorTests.java index 1ac7811ae72..6fe7cde3df2 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/DataSourceHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/DataSourceHealthIndicatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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,7 +24,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.springframework.boot.actuate.health.DataSourceHealthIndicator.Product; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.SingleConnectionDataSource; @@ -104,20 +103,4 @@ public class DataSourceHealthIndicatorTests { verify(connection, times(2)).close(); } - @Test - public void productLookups() throws Exception { - assertThat(Product.forProduct("newone")).isNull(); - assertThat(Product.forProduct("HSQL Database Engine")).isEqualTo(Product.HSQLDB); - assertThat(Product.forProduct("Oracle")).isEqualTo(Product.ORACLE); - assertThat(Product.forProduct("Apache Derby")).isEqualTo(Product.DERBY); - assertThat(Product.forProduct("DB2")).isEqualTo(Product.DB2); - assertThat(Product.forProduct("DB2/LINUXX8664")).isEqualTo(Product.DB2); - assertThat(Product.forProduct("DB2 UDB for AS/400")).isEqualTo(Product.DB2_AS400); - assertThat(Product.forProduct("DB3 XDB for AS/400")).isEqualTo(Product.DB2_AS400); - assertThat(Product.forProduct("Informix Dynamic Server")) - .isEqualTo(Product.INFORMIX); - assertThat(Product.forProduct("Firebird 2.5.WI")).isEqualTo(Product.FIREBIRD); - assertThat(Product.forProduct("Firebird 2.1.LI")).isEqualTo(Product.FIREBIRD); - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java index e81eb99c432..f519bc5764c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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,6 +24,7 @@ import javax.sql.DataSource; import org.springframework.beans.BeanUtils; import org.springframework.beans.MutablePropertyValues; import org.springframework.boot.bind.RelaxedDataBinder; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.util.ClassUtils; /** diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java index d24a5bcced3..9c53811ecfd 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -23,6 +23,7 @@ import com.zaxxer.hikari.HikariDataSource; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.context.annotation.Bean; /** @@ -51,8 +52,15 @@ abstract class DataSourceConfiguration { @ConfigurationProperties("spring.datasource.tomcat") public org.apache.tomcat.jdbc.pool.DataSource dataSource( DataSourceProperties properties) { - return createDataSource(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(); + if (validationQuery != null) { + dataSource.setTestOnBorrow(true); + dataSource.setValidationQuery(validationQuery); + } + return dataSource; } } @@ -76,8 +84,15 @@ abstract class DataSourceConfiguration { @ConfigurationProperties("spring.datasource.dbcp") public org.apache.commons.dbcp.BasicDataSource dataSource( DataSourceProperties properties) { - return createDataSource(properties, + org.apache.commons.dbcp.BasicDataSource dataSource = createDataSource(properties, org.apache.commons.dbcp.BasicDataSource.class); + DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl()); + String validationQuery = databaseDriver.getValidationQuery(); + if (validationQuery != null) { + dataSource.setTestOnBorrow(true); + dataSource.setValidationQuery(validationQuery); + } + return dataSource; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index ce70a9f9d98..0adaf1360ee 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -26,6 +26,7 @@ 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.jdbc.DatabaseDriver; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.util.Assert; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriver.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriver.java deleted file mode 100644 index 030cbef3d8e..00000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriver.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2012-2016 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 org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * Enumeration of common database drivers. - * - * @author Phillip Webb - * @author Maciej Walkowiak - * @author Marten Deinum - * @since 1.2.0 - */ -enum DatabaseDriver { - - /** - * Unknown type. - */ - UNKNOWN(null), - - /** - * Apache Derby. - */ - DERBY("org.apache.derby.jdbc.EmbeddedDriver"), - - /** - * H2. - */ - H2("org.h2.Driver", "org.h2.jdbcx.JdbcDataSource"), - - /** - * HyperSQL DataBase. - */ - HSQLDB("org.hsqldb.jdbc.JDBCDriver", "org.hsqldb.jdbc.pool.JDBCXADataSource"), - - /** - * SQL Lite. - */ - SQLITE("org.sqlite.JDBC"), - - /** - * MySQL. - */ - MYSQL("com.mysql.jdbc.Driver", "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"), - - /** - * Maria DB. - */ - MARIADB("org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource"), - - /** - * Google App Engine. - */ - GOOGLE("com.google.appengine.api.rdbms.AppEngineDriver"), - - /** - * Oracle. - */ - ORACLE("oracle.jdbc.OracleDriver", "oracle.jdbc.xa.client.OracleXADataSource"), - - /** - * Postgres. - */ - POSTGRESQL("org.postgresql.Driver", "org.postgresql.xa.PGXADataSource"), - - /** - * jTDS. - */ - JTDS("net.sourceforge.jtds.jdbc.Driver"), - - /** - * SQL Server. - */ - SQLSERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver", - "com.microsoft.sqlserver.jdbc.SQLServerXADataSource"), - - /** - * Firebird. - */ - FIREBIRD("org.firebirdsql.jdbc.FBDriver", - "org.firebirdsql.pool.FBConnectionPoolDataSource"), - - /** - * DB2 Server. - */ - DB2("com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource"), - - /** - * DB2 AS400 Server. - */ - AS400("com.ibm.as400.access.AS400JDBCDriver", - "com.ibm.as400.access.AS400JDBCXADataSource"), - - /** - * Teradata. - */ - TERADATA("com.teradata.jdbc.TeraDriver"); - - private final String driverClassName; - - private final String xaDataSourceClassName; - - DatabaseDriver(String driverClassName) { - this(driverClassName, null); - } - - DatabaseDriver(String driverClassName, String xaDataSourceClassName) { - this.driverClassName = driverClassName; - this.xaDataSourceClassName = xaDataSourceClassName; - } - - /** - * Return the driver class name. - * @return the class name or {@code null} - */ - public String getDriverClassName() { - return this.driverClassName; - } - - /** - * Return the XA driver source class name. - * @return the class name or {@code null} - */ - public String getXaDataSourceClassName() { - return this.xaDataSourceClassName; - } - - /** - * Find a {@link DatabaseDriver} for the given URL. - * @param url JDBC URL - * @return driver class name or {@link #UNKNOWN} if not found - */ - public static DatabaseDriver fromJdbcUrl(String url) { - if (StringUtils.hasLength(url)) { - Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'"); - String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(); - for (DatabaseDriver driver : values()) { - String prefix = ":" + driver.name().toLowerCase() + ":"; - if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) { - return driver; - } - } - } - return UNKNOWN; - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java index 9212695a07e..95dad6c4365 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/XADataSourceAutoConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.jta.XADataSourceWrapper; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java index 89ae3b75a99..30b2540f0ee 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java @@ -37,6 +37,7 @@ import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -114,27 +115,62 @@ public class DataSourceAutoConfigurationTests { } @Test - public void testHikariIsFallback() throws Exception { - HikariDataSource pool = testDataSourceFallback(HikariDataSource.class, + public void tomcatValidatesConnectionByDefault() { + org.apache.tomcat.jdbc.pool.DataSource dataSource = autoConfigureDataSource( + org.apache.tomcat.jdbc.pool.DataSource.class); + assertThat(dataSource.isTestOnBorrow()).isTrue(); + assertThat(dataSource.getValidationQuery()) + .isEqualTo(DatabaseDriver.HSQLDB.getValidationQuery()); + } + + @Test + public void hikariIsFallback() throws Exception { + HikariDataSource dataSource = autoConfigureDataSource(HikariDataSource.class, "org.apache.tomcat"); - assertThat(pool.getJdbcUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); + assertThat(dataSource.getJdbcUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); + } + + @Test + public void hikariValidatesConnectionByDefault() throws Exception { + HikariDataSource dataSource = autoConfigureDataSource(HikariDataSource.class, + "org.apache.tomcat"); + assertThat(dataSource.getConnectionTestQuery()).isNull(); // Use Connection#isValid() } @Test public void commonsDbcpIsFallback() throws Exception { - BasicDataSource dataSource = testDataSourceFallback(BasicDataSource.class, + BasicDataSource dataSource = autoConfigureDataSource(BasicDataSource.class, "org.apache.tomcat", "com.zaxxer.hikari"); assertThat(dataSource.getUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); } + @Test + public void commonsDbcpValidatesConnectionByDefault() { + BasicDataSource dataSource = autoConfigureDataSource(BasicDataSource.class, + "org.apache.tomcat", "com.zaxxer.hikari"); + assertThat(dataSource.getTestOnBorrow()).isTrue(); + assertThat(dataSource.getValidationQuery()) + .isEqualTo(DatabaseDriver.HSQLDB.getValidationQuery()); + } + @Test public void commonsDbcp2IsFallback() throws Exception { - org.apache.commons.dbcp2.BasicDataSource dataSource = testDataSourceFallback( + org.apache.commons.dbcp2.BasicDataSource dataSource = autoConfigureDataSource( org.apache.commons.dbcp2.BasicDataSource.class, "org.apache.tomcat", "com.zaxxer.hikari", "org.apache.commons.dbcp."); assertThat(dataSource.getUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); } + + @Test + public void commonsDbcp2ValidatesConnectionByDefault() throws Exception { + org.apache.commons.dbcp2.BasicDataSource dataSource = autoConfigureDataSource( + org.apache.commons.dbcp2.BasicDataSource.class, "org.apache.tomcat", + "com.zaxxer.hikari", "org.apache.commons.dbcp."); + assertThat(dataSource.getTestOnBorrow()).isEqualTo(true); + assertThat(dataSource.getValidationQuery()).isNull(); // Use Connection#isValid() + } + @Test public void testEmbeddedTypeDefaultsUsername() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, @@ -169,7 +205,7 @@ public class DataSourceAutoConfigurationTests { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:" + "org.springframework.boot.autoconfigure.jdbc." - + "DataSourceAutoConfigurationTests$DatabaseDriver", + + "DataSourceAutoConfigurationTests$DatabaseTestDriver", "spring.datasource.url:jdbc:foo://localhost"); this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); @@ -178,7 +214,7 @@ public class DataSourceAutoConfigurationTests { assertThat(bean).isNotNull(); org.apache.tomcat.jdbc.pool.DataSource pool = (org.apache.tomcat.jdbc.pool.DataSource) bean; assertThat(pool.getDriverClassName()).isEqualTo( - "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationTests$DatabaseDriver"); + "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationTests$DatabaseTestDriver"); assertThat(pool.getUsername()).isNull(); } @@ -222,7 +258,7 @@ public class DataSourceAutoConfigurationTests { } @SuppressWarnings("unchecked") - private T testDataSourceFallback(Class expectedType, + private T autoConfigureDataSource(Class expectedType, final String... hiddenPackages) { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.hsqldb.jdbcDriver", @@ -267,7 +303,7 @@ public class DataSourceAutoConfigurationTests { } // see testExplicitDriverClassClearsUserName - public static class DatabaseDriver implements Driver { + public static class DatabaseTestDriver implements Driver { @Override public Connection connect(String url, Properties info) throws SQLException { diff --git a/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java b/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java new file mode 100644 index 00000000000..744c5a568dd --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java @@ -0,0 +1,241 @@ +/* + * Copyright 2012-2016 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.jdbc; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Enumeration of common database drivers. + * + * @author Phillip Webb + * @author Maciej Walkowiak + * @author Marten Deinum + * @author Stephane Nicoll + * @since 1.2.0 + */ +public enum DatabaseDriver { + + /** + * Unknown type. + */ + UNKNOWN(null, null), + + /** + * Apache Derby. + */ + DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver", null, + "SELECT 1 FROM SYSIBM.SYSDUMMY1"), + + /** + * H2. + */ + H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"), + + /** + * HyperSQL DataBase. + */ + HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver", + "org.hsqldb.jdbc.pool.JDBCXADataSource", + "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"), + + /** + * SQL Lite. + */ + SQLITE("SQLite", "org.sqlite.JDBC"), + + /** + * MySQL. + */ + MYSQL("MySQL", "com.mysql.jdbc.Driver", + "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", "SELECT 1"), + + /** + * Maria DB. + */ + MARIADB("MySQL", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource", + "SELECT 1"), + + /** + * Google App Engine. + */ + GAE(null, "com.google.appengine.api.rdbms.AppEngineDriver"), + + /** + * Oracle. + */ + ORACLE("Oracle", "oracle.jdbc.OracleDriver", + "oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"), + + /** + * Postgres. + */ + POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource", + "SELECT 1"), + + /** + * jTDS. As it can be used for several databases, there isn't a single product name + * we could rely on. + */ + JTDS(null, "net.sourceforge.jtds.jdbc.Driver"), + + /** + * SQL Server. + */ + SQLSERVER("SQL SERVER", "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1"), + + /** + * Firebird. + */ + FIREBIRD("Firebird", "org.firebirdsql.jdbc.FBDriver", + "org.firebirdsql.pool.FBConnectionPoolDataSource", + "SELECT 1 FROM RDB$DATABASE") { + + @Override + protected boolean matchProductName(String productName) { + return super.matchProductName(productName) + || productName.toLowerCase().startsWith("firebird"); + } + }, + + /** + * DB2 Server. + */ + DB2("DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource", + "SELECT 1 FROM SYSIBM.SYSDUMMY1") { + + @Override + protected boolean matchProductName(String productName) { + return super.matchProductName(productName) + || productName.toLowerCase().startsWith("db2/"); + } + }, + + /** + * DB2 AS400 Server. + */ + DB2_AS400("DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver", + "com.ibm.as400.access.AS400JDBCXADataSource", + "SELECT 1 FROM SYSIBM.SYSDUMMY1") { + + @Override + protected boolean matchProductName(String productName) { + return super.matchProductName(productName) + || productName.toLowerCase().contains("as/400"); + } + }, + + /** + * Teradata. + */ + TERADATA("Teradata", "com.teradata.jdbc.TeraDriver"), + + /** + * Informix. + */ + INFORMIX("Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null, + "select count(*) from systables"); + + private final String productName; + + private final String driverClassName; + + private final String xaDataSourceClassName; + + private final String validationQuery; + + DatabaseDriver(String name, String driverClassName) { + this(name, driverClassName, null); + } + + DatabaseDriver(String name, String driverClassName, String xaDataSourceClassName) { + this(name, driverClassName, xaDataSourceClassName, null); + } + + DatabaseDriver(String productName, String driverClassName, + String xaDataSourceClassName, String validationQuery) { + this.productName = productName; + this.driverClassName = driverClassName; + this.xaDataSourceClassName = xaDataSourceClassName; + this.validationQuery = validationQuery; + } + + protected boolean matchProductName(String productName) { + return this.productName != null && this.productName.equalsIgnoreCase(productName); + } + + /** + * Return the driver class name. + * @return the class name or {@code null} + */ + public String getDriverClassName() { + return this.driverClassName; + } + + /** + * Return the XA driver source class name. + * @return the class name or {@code null} + */ + public String getXaDataSourceClassName() { + return this.xaDataSourceClassName; + } + + /** + * Return the validation query. + * @return the validation query or {@code null} + */ + public String getValidationQuery() { + return this.validationQuery; + } + + /** + * Find a {@link DatabaseDriver} for the given URL. + * @param url JDBC URL + * @return the database driver or {@link #UNKNOWN} if not found + */ + public static DatabaseDriver fromJdbcUrl(String url) { + if (StringUtils.hasLength(url)) { + Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'"); + String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(); + for (DatabaseDriver driver : values()) { + String prefix = ":" + driver.name().toLowerCase() + ":"; + if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) { + return driver; + } + } + } + return UNKNOWN; + } + + /** + * Find a {@link DatabaseDriver} for the given product name. + * @param productName product name + * @return the database driver or {@link #UNKNOWN} if not found + */ + public static DatabaseDriver fromProductName(String productName) { + if (StringUtils.hasLength(productName)) { + for (DatabaseDriver candidate : values()) { + if (candidate.matchProductName(productName)) { + return candidate; + } + } + } + return UNKNOWN; + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriverTests.java b/spring-boot/src/test/java/org/springframework/boot/jdbc/DatabaseDriverTests.java similarity index 51% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriverTests.java rename to spring-boot/src/test/java/org/springframework/boot/jdbc/DatabaseDriverTests.java index 9f7ac2d2ecc..2a9dae91ec9 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DatabaseDriverTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/jdbc/DatabaseDriverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.jdbc; import org.junit.Rule; import org.junit.Test; @@ -49,7 +49,8 @@ public class DatabaseDriverTests { @Test public void unknownOnNullJdbcUrl() { - assertThat(DatabaseDriver.fromJdbcUrl(null)).isEqualTo(DatabaseDriver.UNKNOWN); + DatabaseDriver actual = DatabaseDriver.fromJdbcUrl(null); + assertThat(actual).isEqualTo(DatabaseDriver.UNKNOWN); } @Test @@ -59,4 +60,26 @@ public class DatabaseDriverTests { DatabaseDriver.fromJdbcUrl("malformed:url"); } + @Test + public void unknownOnNullProductName() { + DatabaseDriver actual = DatabaseDriver.fromProductName(null); + assertThat(actual).isEqualTo(DatabaseDriver.UNKNOWN); + } + + @Test + public void databaseProductNameLookups() throws Exception { + assertThat(DatabaseDriver.fromProductName("newone")).isEqualTo(DatabaseDriver.UNKNOWN); + assertThat(DatabaseDriver.fromProductName("HSQL Database Engine")).isEqualTo(DatabaseDriver.HSQLDB); + assertThat(DatabaseDriver.fromProductName("Oracle")).isEqualTo(DatabaseDriver.ORACLE); + assertThat(DatabaseDriver.fromProductName("Apache Derby")).isEqualTo(DatabaseDriver.DERBY); + assertThat(DatabaseDriver.fromProductName("DB2")).isEqualTo(DatabaseDriver.DB2); + assertThat(DatabaseDriver.fromProductName("DB2/LINUXX8664")).isEqualTo(DatabaseDriver.DB2); + assertThat(DatabaseDriver.fromProductName("DB2 UDB for AS/400")).isEqualTo(DatabaseDriver.DB2_AS400); + assertThat(DatabaseDriver.fromProductName("DB3 XDB for AS/400")).isEqualTo(DatabaseDriver.DB2_AS400); + assertThat(DatabaseDriver.fromProductName("Informix Dynamic Server")) + .isEqualTo(DatabaseDriver.INFORMIX); + assertThat(DatabaseDriver.fromProductName("Firebird 2.5.WI")).isEqualTo(DatabaseDriver.FIREBIRD); + assertThat(DatabaseDriver.fromProductName("Firebird 2.1.LI")).isEqualTo(DatabaseDriver.FIREBIRD); + } + }