diff --git a/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoClientSettingsBuilderCustomizer.groovy b/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoClientSettingsBuilderCustomizer.groovy
new file mode 100644
index 00000000000..fd3ff8500be
--- /dev/null
+++ b/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoClientSettingsBuilderCustomizer.groovy
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.grails.datastore.mapping.mongo.connections
+
+import com.mongodb.MongoClientSettings
+
+/**
+ * Callback interface that can be implemented by beans wishing to customize the
+ * {@link MongoClientSettings.Builder} used to construct the underlying
+ * {@link com.mongodb.client.MongoClient} before it is created.
+ *
+ *
Any beans of this type found in the application context are applied, in order,
+ * to the builder for the default connection source. This is the supported extension
+ * point for settings that cannot be expressed through {@code grails.mongodb.*}
+ * configuration — for example registering a driver
+ * {@link com.mongodb.event.CommandListener} for metrics/tracing, tuning the connection
+ * pool, or configuring read/write concerns programmatically.
+ *
+ *
+ * @Bean
+ * MongoClientSettingsBuilderCustomizer mongoMetrics(MeterRegistry registry) {
+ * return { builder -> builder.addCommandListener(new MongoMetricsCommandListener(registry)) }
+ * }
+ *
+ *
+ * Mirrors the semantics of Spring Boot's
+ * {@code MongoClientSettingsBuilderCustomizer}.
+ *
+ * @since 7.2
+ */
+@FunctionalInterface
+interface MongoClientSettingsBuilderCustomizer {
+
+ /**
+ * Customize the {@link MongoClientSettings.Builder}.
+ *
+ * @param builder the builder to customize, never {@code null}
+ */
+ void customize(MongoClientSettings.Builder builder)
+}
diff --git a/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoConnectionSourceFactory.groovy b/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoConnectionSourceFactory.groovy
index 4db4ddce546..df1de862614 100644
--- a/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoConnectionSourceFactory.groovy
+++ b/grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/connections/MongoConnectionSourceFactory.groovy
@@ -63,6 +63,15 @@ class MongoConnectionSourceFactory extends AbstractConnectionSourceFactory codecs = []
+ /**
+ * Optional customizers applied to the {@link MongoClientSettings.Builder} of the
+ * default connection source before the {@link MongoClient} is created. This is the
+ * supported hook for settings that have no {@code grails.mongodb.*} equivalent — e.g.
+ * registering a driver {@link com.mongodb.event.CommandListener} for metrics/tracing.
+ */
+ @Autowired(required = false)
+ List clientSettingsCustomizers = []
+
@Override
Serializable getConnectionSourcesConfigurationKey() {
return MongoSettings.SETTING_CONNECTIONS
@@ -98,6 +107,9 @@ class MongoConnectionSourceFactory extends AbstractConnectionSourceFactory(name, client, settings)
}
diff --git a/grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/config/MongoConnectionSourceFactorySpec.groovy b/grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/config/MongoConnectionSourceFactorySpec.groovy
index 18cc8e669ab..8a5ade99b97 100644
--- a/grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/config/MongoConnectionSourceFactorySpec.groovy
+++ b/grails-data-mongodb/core/src/test/groovy/org/grails/datastore/mapping/mongo/config/MongoConnectionSourceFactorySpec.groovy
@@ -20,10 +20,13 @@ package org.grails.datastore.mapping.mongo.config
import com.mongodb.client.MongoClient
import com.mongodb.ReadPreference
+import com.mongodb.MongoClientSettings
+import com.mongodb.event.CommandListener
import org.grails.datastore.mapping.core.DatastoreUtils
import org.grails.datastore.mapping.core.connections.ConnectionSource
import org.grails.datastore.mapping.core.connections.ConnectionSources
import org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer
+import org.grails.datastore.mapping.mongo.connections.MongoClientSettingsBuilderCustomizer
import org.grails.datastore.mapping.mongo.connections.MongoConnectionSourceFactory
import org.grails.datastore.mapping.mongo.connections.MongoConnectionSourceSettings
import spock.lang.Specification
@@ -82,4 +85,47 @@ class MongoConnectionSourceFactorySpec extends Specification {
sources?.close()
}
+ void "test a registered MongoClientSettingsBuilderCustomizer is applied to the default connection source"() {
+ given: "a customizer that records its invocation and registers a command listener"
+ CommandListener listener = new CommandListener() {}
+ MongoClientSettings.Builder captured = null
+ MongoClientSettingsBuilderCustomizer customizer = { MongoClientSettings.Builder builder ->
+ captured = builder
+ builder.addCommandListener(listener)
+ }
+
+ when: "the factory builds connection sources with the customizer registered"
+ MongoConnectionSourceFactory factory = new MongoConnectionSourceFactory(clientSettingsCustomizers: [customizer])
+ ConnectionSources sources = ConnectionSourcesInitializer.create(factory, DatastoreUtils.createPropertyResolver(
+ (MongoSettings.SETTING_URL): "mongodb://localhost/myDb"
+ ))
+
+ then: "the customizer was invoked and its command listener applied to the built client settings"
+ sources.defaultConnectionSource != null
+ captured != null
+ captured.build().commandListeners.contains(listener)
+
+ cleanup:
+ sources?.close()
+ }
+
+ void "test multiple MongoClientSettingsBuilderCustomizers are applied in registration order"() {
+ given: "two customizers that append to a shared list"
+ List order = []
+ MongoClientSettingsBuilderCustomizer first = { MongoClientSettings.Builder builder -> order << 'first' }
+ MongoClientSettingsBuilderCustomizer second = { MongoClientSettings.Builder builder -> order << 'second' }
+
+ when: "the factory builds connection sources with both customizers registered"
+ MongoConnectionSourceFactory factory = new MongoConnectionSourceFactory(clientSettingsCustomizers: [first, second])
+ ConnectionSources sources = ConnectionSourcesInitializer.create(factory, DatastoreUtils.createPropertyResolver(
+ (MongoSettings.SETTING_URL): "mongodb://localhost/myDb"
+ ))
+
+ then: "both ran, in order"
+ order == ['first', 'second']
+
+ cleanup:
+ sources?.close()
+ }
+
}