From b1bad144c6a12ccb7acfc0a7d1abb8e3118770f3 Mon Sep 17 00:00:00 2001 From: Tim Yates Date: Fri, 26 Apr 2024 11:35:38 +0100 Subject: [PATCH] feat: Allow disabling of datasources by excluding packages (#1323) This allows the testing of non-db beans without requiring the connection pools to be fully configured. Does not cover r2dbc as that is defined elsewhere. https://github.com/micronaut-projects/micronaut-data/issues/2893 It is done this way (with package requirements) as there is no way currently to allow EachBean to skip factory creation based on a property of the bean (ie Toggleable#isEnabled). --- .../configuration/jdbc/dbcp/package-info.java | 5 +- .../dbcp/DatasourceConfigurationSpec.groovy | 36 +++++++------ .../jdbc/hikari/package-info.java | 5 +- .../hikari/DatasourceConfigurationSpec.groovy | 21 ++++++++ .../jdbc/tomcat/package-info.java | 5 +- .../tomcat/DatasourceConfigurationSpec.groovy | 21 ++++++++ .../configuration/jdbc/ucp/package-info.java | 5 +- .../ucp/DatasourceConfigurationSpec.groovy | 30 +++++++++++ src/main/docs/guide/jdbc.adoc | 2 +- src/main/docs/guide/jdbc/jdbc-disable.adoc | 1 + src/main/docs/guide/toc.yml | 1 + .../hibernate6/sync/DisabledDbAppTest.java | 53 ++++++++++++++++++ .../jdbc/ucp/sync/DisabledDbAppTest.java | 54 +++++++++++++++++++ .../example/jooq/sync/DisabledDbAppTest.java | 53 ++++++++++++++++++ 14 files changed, 272 insertions(+), 20 deletions(-) create mode 100644 src/main/docs/guide/jdbc/jdbc-disable.adoc create mode 100644 tests/hibernate6/hibernate6-mysql/src/test/java/example/hibernate6/sync/DisabledDbAppTest.java create mode 100644 tests/jdbc-ucp-tests/jdbc-ucp-oracle/src/test/java/example/jdbc/ucp/sync/DisabledDbAppTest.java create mode 100644 tests/jooq-tests/jooq-jdbc-postgres/src/test/java/example/jooq/sync/DisabledDbAppTest.java diff --git a/jdbc-dbcp/src/main/java/io/micronaut/configuration/jdbc/dbcp/package-info.java b/jdbc-dbcp/src/main/java/io/micronaut/configuration/jdbc/dbcp/package-info.java index f2c4d26bf..0abe72efe 100644 --- a/jdbc-dbcp/src/main/java/io/micronaut/configuration/jdbc/dbcp/package-info.java +++ b/jdbc-dbcp/src/main/java/io/micronaut/configuration/jdbc/dbcp/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 original authors + * Copyright 2017-2024 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ */ @Configuration @Requires(classes = BasicDataSource.class) +@Requires(property = BasicJdbcConfiguration.PREFIX + ".enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) package io.micronaut.configuration.jdbc.dbcp; import io.micronaut.context.annotation.Configuration; import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.jdbc.BasicJdbcConfiguration; import org.apache.commons.dbcp2.BasicDataSource; diff --git a/jdbc-dbcp/src/test/groovy/io/micronaut/configuration/jdbc/dbcp/DatasourceConfigurationSpec.groovy b/jdbc-dbcp/src/test/groovy/io/micronaut/configuration/jdbc/dbcp/DatasourceConfigurationSpec.groovy index 2c2a25942..38c58f5a3 100644 --- a/jdbc-dbcp/src/test/groovy/io/micronaut/configuration/jdbc/dbcp/DatasourceConfigurationSpec.groovy +++ b/jdbc-dbcp/src/test/groovy/io/micronaut/configuration/jdbc/dbcp/DatasourceConfigurationSpec.groovy @@ -1,18 +1,3 @@ -/* - * Copyright 2017-2020 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package io.micronaut.configuration.jdbc.dbcp import io.micronaut.jdbc.DataSourceResolver @@ -69,6 +54,27 @@ class DatasourceConfigurationSpec extends Specification { applicationContext.close() } + void "test datasource can be disabled"() { + given: + ApplicationContext applicationContext = new DefaultApplicationContext("test") + applicationContext.environment.addPropertySource(MapPropertySource.of( + "test", + [ + 'datasources.default': [:], + 'datasources.enabled': false + ] + )) + applicationContext.start() + + expect: + !applicationContext.containsBean(DataSource) + !applicationContext.containsBean(BasicDataSource) + !applicationContext.containsBean(DatasourceConfiguration) + + cleanup: + applicationContext.close() + } + void "test operations with a blank connection"() { given: ApplicationContext applicationContext = new DefaultApplicationContext("test") diff --git a/jdbc-hikari/src/main/java/io/micronaut/configuration/jdbc/hikari/package-info.java b/jdbc-hikari/src/main/java/io/micronaut/configuration/jdbc/hikari/package-info.java index 0460487c3..5916143de 100644 --- a/jdbc-hikari/src/main/java/io/micronaut/configuration/jdbc/hikari/package-info.java +++ b/jdbc-hikari/src/main/java/io/micronaut/configuration/jdbc/hikari/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 original authors + * Copyright 2017-2024 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ */ @Configuration @Requires(classes = HikariDataSource.class) +@Requires(property = BasicJdbcConfiguration.PREFIX + ".enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) package io.micronaut.configuration.jdbc.hikari; import com.zaxxer.hikari.HikariDataSource; import io.micronaut.context.annotation.Configuration; import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.jdbc.BasicJdbcConfiguration; diff --git a/jdbc-hikari/src/test/groovy/io/micronaut/configuration/jdbc/hikari/DatasourceConfigurationSpec.groovy b/jdbc-hikari/src/test/groovy/io/micronaut/configuration/jdbc/hikari/DatasourceConfigurationSpec.groovy index ff276f701..adab9355b 100644 --- a/jdbc-hikari/src/test/groovy/io/micronaut/configuration/jdbc/hikari/DatasourceConfigurationSpec.groovy +++ b/jdbc-hikari/src/test/groovy/io/micronaut/configuration/jdbc/hikari/DatasourceConfigurationSpec.groovy @@ -75,6 +75,27 @@ class DatasourceConfigurationSpec extends Specification { applicationContext.close() } + void "test datasource can be disabled"() { + given: + ApplicationContext applicationContext = new DefaultApplicationContext("test") + applicationContext.environment.addPropertySource(MapPropertySource.of( + 'test', + [ + 'datasources.default': [:], + 'datasources.enabled' : false + ] + )) + applicationContext.start() + + expect: + !applicationContext.containsBean(DataSource) + !applicationContext.containsBean(HikariDataSource) + !applicationContext.containsBean(DatasourceConfiguration) + + cleanup: + applicationContext.close() + } + void "test operations with a blank connection"() { given: ApplicationContext applicationContext = new DefaultApplicationContext("test") diff --git a/jdbc-tomcat/src/main/java/io/micronaut/configuration/jdbc/tomcat/package-info.java b/jdbc-tomcat/src/main/java/io/micronaut/configuration/jdbc/tomcat/package-info.java index 5771c136b..737de8692 100644 --- a/jdbc-tomcat/src/main/java/io/micronaut/configuration/jdbc/tomcat/package-info.java +++ b/jdbc-tomcat/src/main/java/io/micronaut/configuration/jdbc/tomcat/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 original authors + * Copyright 2017-2024 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ */ @Configuration @Requires(classes = DataSource.class) +@Requires(property = BasicJdbcConfiguration.PREFIX + ".enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) package io.micronaut.configuration.jdbc.tomcat; import io.micronaut.context.annotation.Configuration; import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.jdbc.BasicJdbcConfiguration; import org.apache.tomcat.jdbc.pool.DataSource; diff --git a/jdbc-tomcat/src/test/groovy/io/micronaut/configuration/jdbc/tomcat/DatasourceConfigurationSpec.groovy b/jdbc-tomcat/src/test/groovy/io/micronaut/configuration/jdbc/tomcat/DatasourceConfigurationSpec.groovy index 857a2bc10..915196ec7 100644 --- a/jdbc-tomcat/src/test/groovy/io/micronaut/configuration/jdbc/tomcat/DatasourceConfigurationSpec.groovy +++ b/jdbc-tomcat/src/test/groovy/io/micronaut/configuration/jdbc/tomcat/DatasourceConfigurationSpec.groovy @@ -76,6 +76,27 @@ class DatasourceConfigurationSpec extends Specification { applicationContext.close() } + void "test datasource can be disabled"() { + given: + ApplicationContext applicationContext = new DefaultApplicationContext("test") + applicationContext.environment.addPropertySource(MapPropertySource.of( + 'test', + [ + 'datasources.default': [:], + 'datasources.enabled' : false + ] + )) + applicationContext.start() + + expect: + !applicationContext.containsBean(DataSource) + !applicationContext.containsBean(DatasourceConfiguration) + !applicationContext.containsBean(TomcatDataSourcePoolMetadata) + + cleanup: + applicationContext.close() + } + void "test operations with a blank connection"() { given: ApplicationContext applicationContext = new DefaultApplicationContext("test") diff --git a/jdbc-ucp/src/main/java/io/micronaut/configuration/jdbc/ucp/package-info.java b/jdbc-ucp/src/main/java/io/micronaut/configuration/jdbc/ucp/package-info.java index 5a4b1aab4..ce0ad2f10 100644 --- a/jdbc-ucp/src/main/java/io/micronaut/configuration/jdbc/ucp/package-info.java +++ b/jdbc-ucp/src/main/java/io/micronaut/configuration/jdbc/ucp/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 original authors + * Copyright 2017-2024 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ */ @Configuration @Requires(classes = PoolDataSource.class) +@Requires(property = BasicJdbcConfiguration.PREFIX + ".enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) package io.micronaut.configuration.jdbc.ucp; import io.micronaut.context.annotation.Configuration; import io.micronaut.context.annotation.Requires; +import io.micronaut.core.util.StringUtils; +import io.micronaut.jdbc.BasicJdbcConfiguration; import oracle.ucp.jdbc.PoolDataSource; diff --git a/jdbc-ucp/src/test/groovy/io/micronaut/configuration/jdbc/ucp/DatasourceConfigurationSpec.groovy b/jdbc-ucp/src/test/groovy/io/micronaut/configuration/jdbc/ucp/DatasourceConfigurationSpec.groovy index efd513354..7a3d238ad 100644 --- a/jdbc-ucp/src/test/groovy/io/micronaut/configuration/jdbc/ucp/DatasourceConfigurationSpec.groovy +++ b/jdbc-ucp/src/test/groovy/io/micronaut/configuration/jdbc/ucp/DatasourceConfigurationSpec.groovy @@ -18,6 +18,7 @@ package io.micronaut.configuration.jdbc.ucp import io.micronaut.context.ApplicationContext import io.micronaut.context.DefaultApplicationContext import io.micronaut.context.env.MapPropertySource +import io.micronaut.context.exceptions.NoSuchBeanException import io.micronaut.inject.qualifiers.Qualifiers import io.micronaut.jdbc.DataSourceResolver import oracle.ucp.jdbc.PoolDataSource @@ -101,6 +102,35 @@ class DatasourceConfigurationSpec extends Specification { applicationContext.close() } + void "test datasource can be disabled"() { + given: + ApplicationContext applicationContext = new DefaultApplicationContext("test") + applicationContext.environment.addPropertySource(MapPropertySource.of( + 'test', + [ + "datasources.default.url": "jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", + "datasources.default.username": "sa", + "datasources.default.password": "", + "datasources.enabled": false + ] + )) + applicationContext.start() + + expect: + !applicationContext.containsBean(PoolDataSource) + !applicationContext.containsBean(DatasourceConfiguration) + + when: + applicationContext.getBean(DataSource) + + then: + def ex = thrown(NoSuchBeanException) + ex.message.startsWith("No bean of type [javax.sql.DataSource] exists.") + + cleanup: + applicationContext.close() + } + void "test properties are bindable"() { given: String context = UUID.randomUUID().toString() diff --git a/src/main/docs/guide/jdbc.adoc b/src/main/docs/guide/jdbc.adoc index c60a4cd81..51e5467f8 100644 --- a/src/main/docs/guide/jdbc.adoc +++ b/src/main/docs/guide/jdbc.adoc @@ -1,4 +1,4 @@ -Java data sources can be configured for one of three currently provided implementations. Apache DBCP2, Hikari, and Tomcat are supported by default. +Java data sources can be configured for one of four currently provided implementations. Apache DBCP2, Hikari, Tomcat, and Oracle Universal Connection Pool are supported by default. [TIP] .Using the CLI diff --git a/src/main/docs/guide/jdbc/jdbc-disable.adoc b/src/main/docs/guide/jdbc/jdbc-disable.adoc new file mode 100644 index 000000000..a48e27b1a --- /dev/null +++ b/src/main/docs/guide/jdbc/jdbc-disable.adoc @@ -0,0 +1 @@ +You can disable Micronaut Data Sources, for example in a test, by setting `datasources.enabled` to `false`. diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index 02bd4052a..7f271ae64 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -2,6 +2,7 @@ introduction: Introduction releaseHistory: Release History jdbc: title: Configuring JDBC + jdbc-disable: Disable Micronaut JDBC Data Sources jdbc-connection-pools: Configuring JDBC Connection Pools jdbc-multiple-datasources: Configuring Multiple Data Sources jdbc-healthchecks: JDBC Health Checks diff --git a/tests/hibernate6/hibernate6-mysql/src/test/java/example/hibernate6/sync/DisabledDbAppTest.java b/tests/hibernate6/hibernate6-mysql/src/test/java/example/hibernate6/sync/DisabledDbAppTest.java new file mode 100644 index 000000000..9fb79506c --- /dev/null +++ b/tests/hibernate6/hibernate6-mysql/src/test/java/example/hibernate6/sync/DisabledDbAppTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.hibernate6.sync; + +import io.micronaut.context.annotation.Property; +import io.micronaut.context.exceptions.NoSuchBeanException; +import io.micronaut.core.convert.ConversionService; +import io.micronaut.runtime.server.EmbeddedServer; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@MicronautTest +@Property(name = "datasources.enabled", value = "false") +@Property(name = "datasources.default.db-type", value = "mysql") +@Property(name = "jpa.default.properties.hibernate.dialect", value = "org.hibernate.dialect.MySQLDialect") +class DisabledDbAppTest { + + @Test + void serverRunning(EmbeddedServer embeddedServer) { + assertTrue(embeddedServer.isRunning()); + } + + @Test + void canTestNonDbBeans(EmbeddedServer embeddedServer) { + Integer i = embeddedServer.getApplicationContext().getBean(ConversionService.class).convert("10", Integer.class).orElse(999); + assertEquals(10, i); + } + + @Test + void noDatasourceDefined(EmbeddedServer embeddedServer) { + assertThrows(NoSuchBeanException.class, () -> embeddedServer.getApplicationContext().getBean(DataSource.class)); + } +} + diff --git a/tests/jdbc-ucp-tests/jdbc-ucp-oracle/src/test/java/example/jdbc/ucp/sync/DisabledDbAppTest.java b/tests/jdbc-ucp-tests/jdbc-ucp-oracle/src/test/java/example/jdbc/ucp/sync/DisabledDbAppTest.java new file mode 100644 index 000000000..9eb2cae6b --- /dev/null +++ b/tests/jdbc-ucp-tests/jdbc-ucp-oracle/src/test/java/example/jdbc/ucp/sync/DisabledDbAppTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.jdbc.ucp.sync; + +import io.micronaut.context.annotation.Property; +import io.micronaut.context.exceptions.NoSuchBeanException; +import io.micronaut.core.convert.ConversionService; +import io.micronaut.runtime.server.EmbeddedServer; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@MicronautTest +@Property(name = "datasources.enabled", value = "false") +@Property(name = "datasources.default.db-type", value = "oracle") +@Property(name = "datasources.default.connection-factory-class-name", value = "oracle.jdbc.pool.OracleDataSource") +@Property(name = "test-resources.containers.oracle.startup-timeout", value = "600s") +class DisabledDbAppTest { + + @Test + void serverRunning(EmbeddedServer embeddedServer) { + assertTrue(embeddedServer.isRunning()); + } + + @Test + void canTestNonDbBeans(EmbeddedServer embeddedServer) { + Integer i = embeddedServer.getApplicationContext().getBean(ConversionService.class).convert("10", Integer.class).orElse(999); + assertEquals(10, i); + } + + @Test + void noDatasourceDefined(EmbeddedServer embeddedServer) { + assertThrows(NoSuchBeanException.class, () -> embeddedServer.getApplicationContext().getBean(DataSource.class)); + } +} + diff --git a/tests/jooq-tests/jooq-jdbc-postgres/src/test/java/example/jooq/sync/DisabledDbAppTest.java b/tests/jooq-tests/jooq-jdbc-postgres/src/test/java/example/jooq/sync/DisabledDbAppTest.java new file mode 100644 index 000000000..fa1001ca2 --- /dev/null +++ b/tests/jooq-tests/jooq-jdbc-postgres/src/test/java/example/jooq/sync/DisabledDbAppTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.jooq.sync; + +import io.micronaut.context.annotation.Property; +import io.micronaut.context.exceptions.NoSuchBeanException; +import io.micronaut.core.convert.ConversionService; +import io.micronaut.runtime.server.EmbeddedServer; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@MicronautTest +@Property(name = "datasources.enabled", value = "false") +@Property(name = "datasources.default.db-type", value = "postgres") +@Property(name = "jooq.datasources.default.sql-dialect", value = "postgres") +class DisabledDbAppTest { + + @Test + void serverRunning(EmbeddedServer embeddedServer) { + assertTrue(embeddedServer.isRunning()); + } + + @Test + void canTestNonDbBeans(EmbeddedServer embeddedServer) { + Integer i = embeddedServer.getApplicationContext().getBean(ConversionService.class).convert("10", Integer.class).orElse(999); + assertEquals(10, i); + } + + @Test + void noDatasourceDefined(EmbeddedServer embeddedServer) { + assertThrows(NoSuchBeanException.class, () -> embeddedServer.getApplicationContext().getBean(DataSource.class)); + } +} +