diff --git a/module/spring-boot-data-mongodb/build.gradle b/module/spring-boot-data-mongodb/build.gradle index ec47e38cbcc..1eee13e63de 100644 --- a/module/spring-boot-data-mongodb/build.gradle +++ b/module/spring-boot-data-mongodb/build.gradle @@ -19,7 +19,6 @@ plugins { id "org.springframework.boot.auto-configuration" id "org.springframework.boot.configuration-properties" id "org.springframework.boot.deployed" - id "org.springframework.boot.docker-test" id "org.springframework.boot.optional-dependencies" } @@ -35,18 +34,10 @@ dependencies { compileOnly("com.google.code.findbugs:jsr305") optional(project(":core:spring-boot-autoconfigure")) - optional(project(":module:spring-boot-health")) optional(project(":module:spring-boot-reactor")) optional("org.mongodb:mongodb-driver-reactivestreams") optional("org.mongodb:mongodb-driver-sync") - dockerTestImplementation(project(":core:spring-boot-test")) - dockerTestImplementation(project(":test-support:spring-boot-docker-test-support")) - dockerTestImplementation("ch.qos.logback:logback-classic") - dockerTestImplementation("org.junit.jupiter:junit-jupiter") - dockerTestImplementation("org.testcontainers:junit-jupiter") - dockerTestImplementation("org.testcontainers:mongodb") - testCompileOnly("com.fasterxml.jackson.core:jackson-annotations") testImplementation(project(":core:spring-boot-test")) diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicator.java b/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicator.java deleted file mode 100644 index 41867852846..00000000000 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicator.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.data.mongodb.health; - -import org.bson.Document; -import reactor.core.publisher.Mono; - -import org.springframework.boot.health.contributor.AbstractReactiveHealthIndicator; -import org.springframework.boot.health.contributor.Health; -import org.springframework.boot.health.contributor.ReactiveHealthIndicator; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.util.Assert; - -/** - * A {@link ReactiveHealthIndicator} for Mongo. - * - * @author Yulin Qin - * @since 4.0.0 - */ -public class MongoReactiveHealthIndicator extends AbstractReactiveHealthIndicator { - - private final ReactiveMongoTemplate reactiveMongoTemplate; - - public MongoReactiveHealthIndicator(ReactiveMongoTemplate reactiveMongoTemplate) { - super("Mongo health check failed"); - Assert.notNull(reactiveMongoTemplate, "'reactiveMongoTemplate' must not be null"); - this.reactiveMongoTemplate = reactiveMongoTemplate; - } - - @Override - protected Mono doHealthCheck(Health.Builder builder) { - Mono buildInfo = this.reactiveMongoTemplate.executeCommand("{ hello: 1 }"); - return buildInfo.map((document) -> up(builder, document)); - } - - private Health up(Health.Builder builder, Document document) { - return builder.up().withDetail("maxWireVersion", document.getInteger("maxWireVersion")).build(); - } - -} diff --git a/module/spring-boot-data-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/module/spring-boot-data-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 21134753f00..041ff89777d 100644 --- a/module/spring-boot-data-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/module/spring-boot-data-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -2,5 +2,3 @@ org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveRepositoriesAutoConfiguration org.springframework.boot.data.mongodb.autoconfigure.MongoRepositoriesAutoConfiguration -org.springframework.boot.data.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration -org.springframework.boot.data.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration diff --git a/module/spring-boot-mongodb/build.gradle b/module/spring-boot-mongodb/build.gradle index c0b35f0de53..7c4133bb5b6 100644 --- a/module/spring-boot-mongodb/build.gradle +++ b/module/spring-boot-mongodb/build.gradle @@ -33,6 +33,7 @@ dependencies { optional(project(":core:spring-boot-autoconfigure")) optional(project(":core:spring-boot-docker-compose")) optional(project(":core:spring-boot-testcontainers")) + optional(project(":module:spring-boot-health")) optional(project(":module:spring-boot-micrometer-metrics")) optional(project(":module:spring-boot-reactor")) optional("io.netty:netty-transport") @@ -49,6 +50,7 @@ dependencies { testImplementation(project(":core:spring-boot-test")) testImplementation(project(":test-support:spring-boot-test-support")) testImplementation(testFixtures(project(":core:spring-boot-testcontainers"))) + testImplementation("io.projectreactor:reactor-test") testImplementation("org.springframework.data:spring-data-mongodb") testRuntimeOnly("ch.qos.logback:logback-classic") diff --git a/module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorIntegrationTests.java b/module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorIntegrationTests.java similarity index 83% rename from module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorIntegrationTests.java rename to module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorIntegrationTests.java index a016aa7a44c..1822f03da98 100644 --- a/module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorIntegrationTests.java +++ b/module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; @@ -23,6 +23,7 @@ import com.mongodb.ServerApi; import com.mongodb.ServerApiVersion; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; @@ -31,7 +32,6 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.boot.health.contributor.Health; import org.springframework.boot.health.contributor.Status; import org.springframework.boot.testsupport.container.TestImage; -import org.springframework.data.mongodb.core.MongoTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -49,13 +49,21 @@ class MongoHealthIndicatorIntegrationTests { @Test void standardApi() { Health health = mongoHealth(); - assertThat(health.getStatus()).isEqualTo(Status.UP); + assertHealth(health); } @Test void strictV1Api() { Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build()); + assertHealth(health); + } + + private void assertHealth(Health health) { assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()).containsKey("maxWireVersion"); + assertThat(health.getDetails()).hasEntrySatisfying("databases", + (databases) -> assertThat(databases).asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactlyInAnyOrder("local", "admin", "config")); } private Health mongoHealth() { @@ -70,7 +78,7 @@ class MongoHealthIndicatorIntegrationTests { } MongoClientSettings settings = settingsBuilder.build(); MongoClient mongoClient = MongoClients.create(settings); - MongoHealthIndicator healthIndicator = new MongoHealthIndicator(new MongoTemplate(mongoClient, "db")); + MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient); return healthIndicator.health(true); } diff --git a/module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java b/module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java similarity index 83% rename from module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java rename to module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java index 71e89f0a5b5..877333f31ba 100644 --- a/module/spring-boot-data-mongodb/src/dockerTest/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java +++ b/module/spring-boot-mongodb/src/dockerTest/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; import java.time.Duration; @@ -25,6 +25,7 @@ import com.mongodb.ServerApi; import com.mongodb.ServerApiVersion; import com.mongodb.reactivestreams.client.MongoClient; import com.mongodb.reactivestreams.client.MongoClients; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; @@ -33,7 +34,6 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.boot.health.contributor.Health; import org.springframework.boot.health.contributor.Status; import org.springframework.boot.testsupport.container.TestImage; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -51,13 +51,13 @@ class MongoReactiveHealthIndicatorIntegrationTests { @Test void standardApi() { Health health = mongoHealth(); - assertThat(health.getStatus()).isEqualTo(Status.UP); + assertHealth(health); } @Test void strictV1Api() { Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build()); - assertThat(health.getStatus()).isEqualTo(Status.UP); + assertHealth(health); } private Health mongoHealth() { @@ -72,9 +72,16 @@ class MongoReactiveHealthIndicatorIntegrationTests { } MongoClientSettings settings = settingsBuilder.build(); MongoClient mongoClient = MongoClients.create(settings); - MongoReactiveHealthIndicator healthIndicator = new MongoReactiveHealthIndicator( - new ReactiveMongoTemplate(mongoClient, "db")); + MongoReactiveHealthIndicator healthIndicator = new MongoReactiveHealthIndicator(mongoClient); return healthIndicator.health(true).block(Duration.ofSeconds(30)); } + private void assertHealth(Health health) { + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()).containsKey("maxWireVersion"); + assertThat(health.getDetails()).hasEntrySatisfying("databases", + (databases) -> assertThat(databases).asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactlyInAnyOrder("local", "admin", "config")); + } + } diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java similarity index 77% rename from module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java rename to module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java index 115d18fbaf9..7a740fee979 100644 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoHealthContributorAutoConfiguration.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.autoconfigure.health; +package org.springframework.boot.mongodb.autoconfigure.health; + +import com.mongodb.client.MongoClient; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -22,14 +24,12 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration; -import org.springframework.boot.data.mongodb.health.MongoHealthIndicator; import org.springframework.boot.health.autoconfigure.contributor.CompositeHealthContributorConfiguration; import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.health.contributor.HealthContributor; import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; +import org.springframework.boot.mongodb.health.MongoHealthIndicator; import org.springframework.context.annotation.Bean; -import org.springframework.data.mongodb.core.MongoTemplate; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link MongoHealthIndicator}. @@ -37,13 +37,12 @@ import org.springframework.data.mongodb.core.MongoTemplate; * @author Stephane Nicoll * @since 4.0.0 */ -@AutoConfiguration(after = { MongoReactiveHealthContributorAutoConfiguration.class, MongoDataAutoConfiguration.class, - MongoAutoConfiguration.class }) -@ConditionalOnClass({ MongoTemplate.class, MongoHealthIndicator.class, ConditionalOnEnabledHealthIndicator.class }) -@ConditionalOnBean(MongoTemplate.class) +@AutoConfiguration(after = { MongoReactiveHealthContributorAutoConfiguration.class, MongoAutoConfiguration.class }) +@ConditionalOnClass({ MongoClient.class, MongoHealthIndicator.class, ConditionalOnEnabledHealthIndicator.class }) +@ConditionalOnBean(MongoClient.class) @ConditionalOnEnabledHealthIndicator("mongodb") public final class MongoHealthContributorAutoConfiguration - extends CompositeHealthContributorConfiguration { + extends CompositeHealthContributorConfiguration { MongoHealthContributorAutoConfiguration() { super(MongoHealthIndicator::new); @@ -52,7 +51,7 @@ public final class MongoHealthContributorAutoConfiguration @Bean @ConditionalOnMissingBean(name = { "mongoHealthIndicator", "mongoHealthContributor" }) HealthContributor mongoHealthContributor(ConfigurableListableBeanFactory beanFactory) { - return createContributor(beanFactory, MongoTemplate.class); + return createContributor(beanFactory, MongoClient.class); } } diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java similarity index 76% rename from module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java rename to module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java index 8c0fe6b2d86..80e2da9a528 100644 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfiguration.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.autoconfigure.health; +package org.springframework.boot.mongodb.autoconfigure.health; +import com.mongodb.reactivestreams.client.MongoClient; import reactor.core.publisher.Flux; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -24,13 +25,12 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration; -import org.springframework.boot.data.mongodb.health.MongoReactiveHealthIndicator; import org.springframework.boot.health.autoconfigure.contributor.CompositeReactiveHealthContributorConfiguration; import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.health.contributor.ReactiveHealthContributor; +import org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration; +import org.springframework.boot.mongodb.health.MongoReactiveHealthIndicator; import org.springframework.context.annotation.Bean; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; /** * {@link EnableAutoConfiguration Auto-configuration} for @@ -39,13 +39,13 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate; * @author Stephane Nicoll * @since 4.0.0 */ -@AutoConfiguration(after = MongoReactiveDataAutoConfiguration.class) -@ConditionalOnClass({ ReactiveMongoTemplate.class, Flux.class, MongoReactiveHealthIndicator.class, +@AutoConfiguration(after = MongoReactiveAutoConfiguration.class) +@ConditionalOnClass({ MongoClient.class, Flux.class, MongoReactiveHealthIndicator.class, ConditionalOnEnabledHealthIndicator.class }) -@ConditionalOnBean(ReactiveMongoTemplate.class) +@ConditionalOnBean(MongoClient.class) @ConditionalOnEnabledHealthIndicator("mongodb") public final class MongoReactiveHealthContributorAutoConfiguration - extends CompositeReactiveHealthContributorConfiguration { + extends CompositeReactiveHealthContributorConfiguration { MongoReactiveHealthContributorAutoConfiguration() { super(MongoReactiveHealthIndicator::new); @@ -54,7 +54,7 @@ public final class MongoReactiveHealthContributorAutoConfiguration @Bean @ConditionalOnMissingBean(name = { "mongoHealthIndicator", "mongoHealthContributor" }) ReactiveHealthContributor mongoHealthContributor(ConfigurableListableBeanFactory beanFactory) { - return createContributor(beanFactory, ReactiveMongoTemplate.class); + return createContributor(beanFactory, MongoClient.class); } } diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/package-info.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/package-info.java similarity index 91% rename from module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/package-info.java rename to module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/package-info.java index e22e168a0c1..8d8e7a24042 100644 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/autoconfigure/health/package-info.java +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/autoconfigure/health/package-info.java @@ -18,6 +18,6 @@ * Auto-configuration for Spring Data MongoDB health. */ @NullMarked -package org.springframework.boot.data.mongodb.autoconfigure.health; +package org.springframework.boot.mongodb.autoconfigure.health; import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicator.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoHealthIndicator.java similarity index 57% rename from module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicator.java rename to module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoHealthIndicator.java index 005df6362de..541a9a996f5 100644 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicator.java +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoHealthIndicator.java @@ -14,37 +14,51 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.mongodb.client.MongoClient; import org.bson.Document; import org.springframework.boot.health.contributor.AbstractHealthIndicator; import org.springframework.boot.health.contributor.Health; import org.springframework.boot.health.contributor.HealthIndicator; -import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.util.Assert; /** * Simple implementation of a {@link HealthIndicator} returning status information for - * Mongo data stores. + * MongoDB. * * @author Christian Dupuis * @since 4.0.0 */ public class MongoHealthIndicator extends AbstractHealthIndicator { - private final MongoTemplate mongoTemplate; + private static final Document HELLO_COMMAND = Document.parse("{ hello: 1 }"); - public MongoHealthIndicator(MongoTemplate mongoTemplate) { + private final MongoClient mongoClient; + + public MongoHealthIndicator(MongoClient mongoClient) { super("MongoDB health check failed"); - Assert.notNull(mongoTemplate, "'mongoTemplate' must not be null"); - this.mongoTemplate = mongoTemplate; + Assert.notNull(mongoClient, "'mongoClient' must not be null"); + this.mongoClient = mongoClient; } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { - Document result = this.mongoTemplate.executeCommand("{ hello: 1 }"); - builder.up().withDetail("maxWireVersion", result.getInteger("maxWireVersion")); + Map details = new LinkedHashMap<>(); + List databases = new ArrayList<>(); + details.put("databases", databases); + this.mongoClient.listDatabaseNames().forEach((database) -> { + Document result = this.mongoClient.getDatabase(database).runCommand(HELLO_COMMAND); + databases.add(database); + details.putIfAbsent("maxWireVersion", result.getInteger("maxWireVersion")); + }); + builder.up().withDetails(details); } } diff --git a/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicator.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicator.java new file mode 100644 index 00000000000..c73cdd50e6f --- /dev/null +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicator.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.mongodb.health; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.mongodb.reactivestreams.client.MongoClient; +import org.bson.Document; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.boot.health.contributor.AbstractReactiveHealthIndicator; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.ReactiveHealthIndicator; +import org.springframework.util.Assert; + +/** + * A {@link ReactiveHealthIndicator} for Mongo. + * + * @author Yulin Qin + * @since 4.0.0 + */ +public class MongoReactiveHealthIndicator extends AbstractReactiveHealthIndicator { + + private static final Document HELLO_COMMAND = Document.parse("{ hello: 1 }"); + + private final MongoClient mongoClient; + + public MongoReactiveHealthIndicator(MongoClient mongoClient) { + super("Mongo health check failed"); + Assert.notNull(mongoClient, "'mongoClient' must not be null"); + this.mongoClient = mongoClient; + } + + @Override + protected Mono doHealthCheck(Health.Builder builder) { + Mono> healthDetails = Flux.from(this.mongoClient.listDatabaseNames()) + .flatMap((database) -> Mono.from(this.mongoClient.getDatabase(database).runCommand(HELLO_COMMAND)) + .map((document) -> new HelloResponse(database, document))) + .collectList() + .map((responses) -> { + Map databaseDetails = new LinkedHashMap<>(); + List databases = new ArrayList<>(); + databaseDetails.put("databases", databases); + for (HelloResponse response : responses) { + databases.add(response.database()); + databaseDetails.putIfAbsent("maxWireVersion", response.document().getInteger("maxWireVersion")); + } + return databaseDetails; + }); + return healthDetails.map((details) -> builder.up().withDetails(details).build()); + } + + private record HelloResponse(String database, Document document) { + } + +} diff --git a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/package-info.java b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/package-info.java similarity index 93% rename from module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/package-info.java rename to module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/package-info.java index 4d93eff6db8..b96956377a9 100644 --- a/module/spring-boot-data-mongodb/src/main/java/org/springframework/boot/data/mongodb/health/package-info.java +++ b/module/spring-boot-mongodb/src/main/java/org/springframework/boot/mongodb/health/package-info.java @@ -18,6 +18,6 @@ * MongoDB health integration using Spring Data MongoDB. */ @NullMarked -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/module/spring-boot-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 4038ff363e1..f86289afe52 100644 --- a/module/spring-boot-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/module/spring-boot-mongodb/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,5 @@ org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration +org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration +org.springframework.boot.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration org.springframework.boot.mongodb.autoconfigure.metrics.MongoMetricsAutoConfiguration diff --git a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java similarity index 90% rename from module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java rename to module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java index 2f511e97bd2..dd6517ef40b 100644 --- a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java +++ b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoHealthContributorAutoConfigurationTests.java @@ -19,10 +19,10 @@ package org.springframework.boot.data.mongodb.autoconfigure.health; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration; -import org.springframework.boot.data.mongodb.health.MongoHealthIndicator; import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration; import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; +import org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration; +import org.springframework.boot.mongodb.health.MongoHealthIndicator; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; class MongoHealthContributorAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class)); @Test diff --git a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java similarity index 84% rename from module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java rename to module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java index 432be660ae5..364fe01a61c 100644 --- a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java +++ b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/data/mongodb/autoconfigure/health/MongoReactiveHealthContributorAutoConfigurationTests.java @@ -19,13 +19,13 @@ package org.springframework.boot.data.mongodb.autoconfigure.health; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration; -import org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration; -import org.springframework.boot.data.mongodb.health.MongoHealthIndicator; -import org.springframework.boot.data.mongodb.health.MongoReactiveHealthIndicator; import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration; import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; import org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration; +import org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration; +import org.springframework.boot.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration; +import org.springframework.boot.mongodb.health.MongoHealthIndicator; +import org.springframework.boot.mongodb.health.MongoReactiveHealthIndicator; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -38,8 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat; class MongoReactiveHealthContributorAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, - MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoReactiveAutoConfiguration.class, MongoReactiveHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class)); @Test diff --git a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorTests.java b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorTests.java similarity index 61% rename from module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorTests.java rename to module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorTests.java index 45dd1be26a5..27d1f4f2a96 100644 --- a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoHealthIndicatorTests.java +++ b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoHealthIndicatorTests.java @@ -14,51 +14,67 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; + +import java.util.List; +import java.util.function.Consumer; import com.mongodb.MongoException; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.MongoIterable; import org.bson.Document; import org.junit.jupiter.api.Test; import org.springframework.boot.health.contributor.Health; import org.springframework.boot.health.contributor.Status; -import org.springframework.data.mongodb.core.MongoTemplate; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.BDDMockito.willAnswer; import static org.mockito.Mockito.mock; /** * Tests for {@link MongoHealthIndicator}. * * @author Christian Dupuis + * @author Andy Wilkinson */ class MongoHealthIndicatorTests { @Test + @SuppressWarnings("unchecked") void mongoIsUp() { Document commandResult = mock(Document.class); given(commandResult.getInteger("maxWireVersion")).willReturn(10); - MongoTemplate mongoTemplate = mock(MongoTemplate.class); - given(mongoTemplate.executeCommand("{ hello: 1 }")).willReturn(commandResult); - MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate); + MongoClient mongoClient = mock(MongoClient.class); + MongoIterable databaseNames = mock(MongoIterable.class); + willAnswer((invocation) -> { + ((Consumer) invocation.getArgument(0)).accept("db"); + return null; + }).given(databaseNames).forEach(any()); + given(mongoClient.listDatabaseNames()).willReturn(databaseNames); + MongoDatabase mongoDatabase = mock(MongoDatabase.class); + given(mongoClient.getDatabase("db")).willReturn(mongoDatabase); + given(mongoDatabase.runCommand(Document.parse("{ hello: 1 }"))).willReturn(commandResult); + MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient); Health health = healthIndicator.health(); assertThat(health.getStatus()).isEqualTo(Status.UP); assertThat(health.getDetails()).containsEntry("maxWireVersion", 10); + assertThat(health.getDetails()).containsEntry("databases", List.of("db")); then(commandResult).should().getInteger("maxWireVersion"); - then(mongoTemplate).should().executeCommand("{ hello: 1 }"); } @Test void mongoIsDown() { - MongoTemplate mongoTemplate = mock(MongoTemplate.class); - given(mongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed")); - MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate); + MongoClient mongoClient = mock(MongoClient.class); + given(mongoClient.listDatabaseNames()).willThrow(new MongoException("Connection failed")); + MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient); Health health = healthIndicator.health(); assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat((String) health.getDetails().get("error")).contains("Connection failed"); - then(mongoTemplate).should().executeCommand("{ hello: 1 }"); } } diff --git a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorTests.java b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorTests.java similarity index 65% rename from module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorTests.java rename to module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorTests.java index 5c956f6ff4e..f6f115e40a4 100644 --- a/module/spring-boot-data-mongodb/src/test/java/org/springframework/boot/data/mongodb/health/MongoReactiveHealthIndicatorTests.java +++ b/module/spring-boot-mongodb/src/test/java/org/springframework/boot/mongodb/health/MongoReactiveHealthIndicatorTests.java @@ -14,11 +14,14 @@ * limitations under the License. */ -package org.springframework.boot.data.mongodb.health; +package org.springframework.boot.mongodb.health; import java.time.Duration; +import java.util.List; import com.mongodb.MongoException; +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoDatabase; import org.bson.Document; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; @@ -26,7 +29,6 @@ import reactor.test.StepVerifier; import org.springframework.boot.health.contributor.Health; import org.springframework.boot.health.contributor.Status; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -40,27 +42,29 @@ import static org.mockito.Mockito.mock; class MongoReactiveHealthIndicatorTests { @Test - void testMongoIsUp() { - Document buildInfo = mock(Document.class); - given(buildInfo.getInteger("maxWireVersion")).willReturn(10); - ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class); - given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willReturn(Mono.just(buildInfo)); - MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator( - reactiveMongoTemplate); + void mongoIsUp() { + MongoClient mongoClient = mock(MongoClient.class); + given(mongoClient.listDatabaseNames()).willReturn(Mono.just("db")); + MongoDatabase mongoDatabase = mock(MongoDatabase.class); + given(mongoClient.getDatabase("db")).willReturn(mongoDatabase); + Document commandResult = mock(Document.class); + given(mongoDatabase.runCommand(Document.parse("{ hello: 1 }"))).willReturn(Mono.just(commandResult)); + given(commandResult.getInteger("maxWireVersion")).willReturn(10); + MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(mongoClient); Mono health = mongoReactiveHealthIndicator.health(); StepVerifier.create(health).consumeNextWith((h) -> { assertThat(h.getStatus()).isEqualTo(Status.UP); - assertThat(h.getDetails()).containsOnlyKeys("maxWireVersion"); + assertThat(h.getDetails()).containsOnlyKeys("maxWireVersion", "databases"); assertThat(h.getDetails()).containsEntry("maxWireVersion", 10); + assertThat(h.getDetails()).containsEntry("databases", List.of("db")); }).expectComplete().verify(Duration.ofSeconds(30)); } @Test - void testMongoIsDown() { - ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class); - given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed")); - MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator( - reactiveMongoTemplate); + void mongoIsDown() { + MongoClient mongoClient = mock(MongoClient.class); + given(mongoClient.listDatabaseNames()).willThrow(new MongoException("Connection failed")); + MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(mongoClient); Mono health = mongoReactiveHealthIndicator.health(); StepVerifier.create(health).consumeNextWith((h) -> { assertThat(h.getStatus()).isEqualTo(Status.DOWN);