From 9753f28a70f0249c23e89ee7790b55dc077489ad Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 6 May 2025 00:03:23 -0700 Subject: [PATCH 01/26] Add Client Metadata update support. --- .../AbstractMultiServerCluster.java | 7 +++- .../internal/connection/BaseCluster.java | 18 +++++++-- .../connection/ClientMetadataHelper.java | 25 ++++++++++++ .../mongodb/internal/connection/Cluster.java | 2 + .../connection/DefaultClusterFactory.java | 16 ++++---- .../DefaultClusterableServerFactory.java | 18 ++++----- .../connection/DnsMultiServerCluster.java | 6 ++- .../InternalStreamConnectionFactory.java | 16 ++++---- .../connection/LoadBalancedCluster.java | 8 ++++ .../LoadBalancedClusterableServerFactory.java | 10 +---- .../connection/MultiServerCluster.java | 5 ++- .../connection/SingleServerCluster.java | 5 ++- .../com/mongodb/ClusterFixture.java | 2 + .../ClientMetadataHelperProseTest.java | 39 +++++++++++++++++++ .../connection/PlainAuthenticatorTest.java | 5 ++- .../connection/SingleServerClusterTest.java | 5 ++- .../AbstractConnectionPoolTest.java | 7 ++-- ...tractServerDiscoveryAndMonitoringTest.java | 7 ++-- .../InitialDnsSeedListDiscoveryProseTest.java | 2 + .../connection/LoadBalancedClusterTest.java | 30 +++++++------- .../connection/SrvPollingProseTests.java | 3 +- .../coroutine/syncadapter/SyncMongoClient.kt | 7 +++- .../client/syncadapter/SyncMongoClient.kt | 6 ++- .../client/syncadapter/SyncMongoClient.java | 8 +++- .../main/com/mongodb/client/MongoClient.java | 3 ++ .../main/com/mongodb/client/MongoCluster.java | 2 +- .../client/internal/MongoClientImpl.java | 5 +++ 27 files changed, 191 insertions(+), 76 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java b/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java index 137a2f266e3..35a01e39e18 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java @@ -77,8 +77,11 @@ private ServerTuple(final ClusterableServer server, final ServerDescription desc } } - AbstractMultiServerCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory) { - super(clusterId, settings, serverFactory); + AbstractMultiServerCluster(final ClusterId clusterId, + final ClusterSettings settings, + final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata) { + super(clusterId, settings, serverFactory, clientMetadata); isTrue("connection mode is multiple", settings.getMode() == MULTIPLE); clusterType = settings.getRequiredClusterType(); replicaSetName = settings.getRequiredReplicaSetName(); diff --git a/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java b/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java index df3e4d1c1fe..63d52ca70c0 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java @@ -55,11 +55,11 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.stream.Stream; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Stream; import static com.mongodb.assertions.Assertions.assertNotNull; import static com.mongodb.assertions.Assertions.isTrue; @@ -101,19 +101,24 @@ abstract class BaseCluster implements Cluster { private final ClusterListener clusterListener; private final Deque waitQueue = new ConcurrentLinkedDeque<>(); private final ClusterClock clusterClock = new ClusterClock(); + private final ClientMetadata clientMetadata; private Thread waitQueueHandler; private volatile boolean isClosed; private volatile ClusterDescription description; - BaseCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory) { + BaseCluster(final ClusterId clusterId, + final ClusterSettings settings, + final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata) { this.clusterId = notNull("clusterId", clusterId); this.settings = notNull("settings", settings); this.serverFactory = notNull("serverFactory", serverFactory); this.clusterListener = singleClusterListener(settings); - clusterListener.clusterOpening(new ClusterOpeningEvent(clusterId)); - description = new ClusterDescription(settings.getMode(), ClusterType.UNKNOWN, Collections.emptyList(), + this.clusterListener.clusterOpening(new ClusterOpeningEvent(clusterId)); + this.description = new ClusterDescription(settings.getMode(), ClusterType.UNKNOWN, Collections.emptyList(), settings, serverFactory.getSettings()); + this.clientMetadata = clientMetadata; } @Override @@ -121,6 +126,11 @@ public ClusterClock getClock() { return clusterClock; } + @Override + public ClientMetadata getClientMetadata() { + return clientMetadata; + } + @Override public ServerTuple selectServer(final ServerSelector serverSelector, final OperationContext operationContext) { isTrue("open", !isClosed()); diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index 825af685c10..a7a0a8e8f57 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -180,6 +180,31 @@ static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE; } + public static BsonDocument updateClientMedataDocument(final BsonDocument clientMetadataDocument, + final MongoDriverInformation mongoDriverInformation) { + BsonDocument updatedClientMetadataDocument = clientMetadataDocument.clone(); + BsonDocument driverInformation = clientMetadataDocument.getDocument("driver"); + + MongoDriverInformation.Builder builder = MongoDriverInformation.builder(mongoDriverInformation) + .driverName(driverInformation.getString("name").getValue()) + .driverVersion(driverInformation.getString("version").getValue()); + + if (updatedClientMetadataDocument.containsKey("platform")) { + builder.driverPlatform(updatedClientMetadataDocument.getString("platform").getValue()); + } + + MongoDriverInformation updatedDriverInformation = builder.build(); + + tryWithLimit(updatedClientMetadataDocument, d -> { + putAtPath(d, "driver.name", listToString(updatedDriverInformation.getDriverNames())); + putAtPath(d, "driver.version", listToString(updatedDriverInformation.getDriverVersions())); + }); + tryWithLimit(updatedClientMetadataDocument, d -> { + putAtPath(d, "platform", listToString(updatedDriverInformation.getDriverPlatforms())); + }); + return updatedClientMetadataDocument; + } + public enum ContainerRuntime { DOCKER("docker") { @Override diff --git a/driver-core/src/main/com/mongodb/internal/connection/Cluster.java b/driver-core/src/main/com/mongodb/internal/connection/Cluster.java index 87fa73c8536..ba154b48308 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/Cluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/Cluster.java @@ -57,6 +57,8 @@ public interface Cluster extends Closeable { */ ClusterClock getClock(); + ClientMetadata getClientMetadata(); + ServerTuple selectServer(ServerSelector serverSelector, OperationContext operationContext); void selectServerAsync(ServerSelector serverSelector, OperationContext operationContext, diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java index 5fb6de6f69a..ac853cb002e 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java @@ -107,27 +107,29 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina InternalOperationContextFactory heartBeatOperationContextFactory = new InternalOperationContextFactory(heartbeatTimeoutSettings, serverApi); + ClientMetadata clientMetadata = new ClientMetadata( + applicationName, + mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build()); + if (clusterSettings.getMode() == ClusterConnectionMode.LOAD_BALANCED) { ClusterableServerFactory serverFactory = new LoadBalancedClusterableServerFactory(serverSettings, connectionPoolSettings, internalConnectionPoolSettings, streamFactory, credential, loggerSettings, commandListener, - applicationName, mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList, serverApi, clusterOperationContextFactory); - return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, clientMetadata, dnsSrvRecordMonitorFactory); } else { ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(serverSettings, connectionPoolSettings, internalConnectionPoolSettings, clusterOperationContextFactory, streamFactory, heartBeatOperationContextFactory, heartbeatStreamFactory, credential, - loggerSettings, commandListener, applicationName, - mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList, + loggerSettings, commandListener, compressorList, serverApi, FaasEnvironment.getFaasEnvironment() != FaasEnvironment.UNKNOWN); if (clusterSettings.getMode() == ClusterConnectionMode.SINGLE) { - return new SingleServerCluster(clusterId, clusterSettings, serverFactory); + return new SingleServerCluster(clusterId, clusterSettings, serverFactory, clientMetadata); } else if (clusterSettings.getMode() == ClusterConnectionMode.MULTIPLE) { if (clusterSettings.getSrvHost() == null) { - return new MultiServerCluster(clusterId, clusterSettings, serverFactory); + return new MultiServerCluster(clusterId, clusterSettings, serverFactory, clientMetadata); } else { - return new DnsMultiServerCluster(clusterId, clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + return new DnsMultiServerCluster(clusterId, clusterSettings, serverFactory, clientMetadata, dnsSrvRecordMonitorFactory); } } else { throw new UnsupportedOperationException("Unsupported cluster mode: " + clusterSettings.getMode()); diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java index aa8973ec092..cb9830c4017 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java @@ -19,7 +19,6 @@ import com.mongodb.LoggerSettings; import com.mongodb.MongoCompressor; import com.mongodb.MongoCredential; -import com.mongodb.MongoDriverInformation; import com.mongodb.ServerAddress; import com.mongodb.ServerApi; import com.mongodb.connection.ClusterConnectionMode; @@ -50,8 +49,6 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory private final MongoCredentialWithCache credential; private final LoggerSettings loggerSettings; private final CommandListener commandListener; - private final String applicationName; - private final MongoDriverInformation mongoDriverInformation; private final List compressorList; @Nullable private final ServerApi serverApi; @@ -63,8 +60,7 @@ public DefaultClusterableServerFactory( final InternalOperationContextFactory clusterOperationContextFactory, final StreamFactory streamFactory, final InternalOperationContextFactory heartbeatOperationContextFactory, final StreamFactory heartbeatStreamFactory, @Nullable final MongoCredential credential, final LoggerSettings loggerSettings, - @Nullable final CommandListener commandListener, @Nullable final String applicationName, - @Nullable final MongoDriverInformation mongoDriverInformation, + @Nullable final CommandListener commandListener, final List compressorList, @Nullable final ServerApi serverApi, final boolean isFunctionAsAServiceEnvironment) { this.serverSettings = serverSettings; this.connectionPoolSettings = connectionPoolSettings; @@ -76,8 +72,6 @@ public DefaultClusterableServerFactory( this.credential = credential == null ? null : new MongoCredentialWithCache(credential); this.loggerSettings = loggerSettings; this.commandListener = commandListener; - this.applicationName = applicationName; - this.mongoDriverInformation = mongoDriverInformation; this.compressorList = compressorList; this.serverApi = serverApi; this.isFunctionAsAServiceEnvironment = isFunctionAsAServiceEnvironment; @@ -88,15 +82,17 @@ public ClusterableServer create(final Cluster cluster, final ServerAddress serve ServerId serverId = new ServerId(cluster.getClusterId(), serverAddress); ClusterConnectionMode clusterMode = cluster.getSettings().getMode(); SameObjectProvider sdamProvider = SameObjectProvider.uninitialized(); + ClientMetadata clientMetadata = cluster.getClientMetadata(); + ServerMonitor serverMonitor = new DefaultServerMonitor(serverId, serverSettings, // no credentials, compressor list, or command listener for the server monitor factory - new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, applicationName, - mongoDriverInformation, emptyList(), loggerSettings, null, serverApi), + new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, clientMetadata, + emptyList(), loggerSettings, null, serverApi), clusterMode, serverApi, isFunctionAsAServiceEnvironment, sdamProvider, heartbeatOperationContextFactory); ConnectionPool connectionPool = new DefaultConnectionPool(serverId, - new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, applicationName, - mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi), + new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, clientMetadata, + compressorList, loggerSettings, commandListener, serverApi), connectionPoolSettings, internalConnectionPoolSettings, sdamProvider, clusterOperationContextFactory); ServerListener serverListener = singleServerListener(serverSettings); SdamServerDescriptionManager sdam = new DefaultSdamServerDescriptionManager(cluster, serverId, serverListener, serverMonitor, diff --git a/driver-core/src/main/com/mongodb/internal/connection/DnsMultiServerCluster.java b/driver-core/src/main/com/mongodb/internal/connection/DnsMultiServerCluster.java index 51e28ee5c84..e165146dd29 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DnsMultiServerCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DnsMultiServerCluster.java @@ -40,9 +40,11 @@ public final class DnsMultiServerCluster extends AbstractMultiServerCluster { private final DnsSrvRecordMonitor dnsSrvRecordMonitor; private volatile MongoException srvResolutionException; - public DnsMultiServerCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory, + public DnsMultiServerCluster(final ClusterId clusterId, final ClusterSettings settings, + final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata, final DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory) { - super(clusterId, settings, serverFactory); + super(clusterId, settings, serverFactory, clientMetadata); dnsSrvRecordMonitor = dnsSrvRecordMonitorFactory.create(assertNotNull(settings.getSrvHost()), settings.getSrvServiceName(), new DnsSrvRecordInitializer() { private volatile boolean initialized; diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java index 8b5c840c501..c4bf015ffe3 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java @@ -19,24 +19,22 @@ import com.mongodb.AuthenticationMechanism; import com.mongodb.LoggerSettings; import com.mongodb.MongoCompressor; -import com.mongodb.MongoDriverInformation; import com.mongodb.ServerApi; import com.mongodb.connection.ClusterConnectionMode; import com.mongodb.connection.ServerId; import com.mongodb.event.CommandListener; import com.mongodb.lang.Nullable; -import org.bson.BsonDocument; import java.util.List; import static com.mongodb.assertions.Assertions.notNull; -import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; class InternalStreamConnectionFactory implements InternalConnectionFactory { private final ClusterConnectionMode clusterConnectionMode; private final boolean isMonitoringConnection; private final StreamFactory streamFactory; - private final BsonDocument clientMetadataDocument; + //TODO UPDATE + private final ClientMetadata clientMetadata; private final List compressorList; private final LoggerSettings loggerSettings; private final CommandListener commandListener; @@ -47,17 +45,17 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode, final StreamFactory streamFactory, @Nullable final MongoCredentialWithCache credential, - @Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation, + final ClientMetadata clientMetadata, final List compressorList, final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) { - this(clusterConnectionMode, false, streamFactory, credential, applicationName, mongoDriverInformation, compressorList, + this(clusterConnectionMode, false, streamFactory, credential, clientMetadata, compressorList, loggerSettings, commandListener, serverApi); } InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode, final boolean isMonitoringConnection, final StreamFactory streamFactory, @Nullable final MongoCredentialWithCache credential, - @Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation, + final ClientMetadata clientMetadata, final List compressorList, final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) { this.clusterConnectionMode = clusterConnectionMode; @@ -67,7 +65,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { this.loggerSettings = loggerSettings; this.commandListener = commandListener; this.serverApi = serverApi; - this.clientMetadataDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); + this.clientMetadata = clientMetadata; this.credential = credential; } @@ -75,7 +73,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { public InternalConnection create(final ServerId serverId, final ConnectionGenerationSupplier connectionGenerationSupplier) { Authenticator authenticator = credential == null ? null : createAuthenticator(credential); InternalStreamConnectionInitializer connectionInitializer = new InternalStreamConnectionInitializer( - clusterConnectionMode, authenticator, clientMetadataDocument, compressorList, serverApi); + clusterConnectionMode, authenticator, clientMetadata.getClientMetadataBsonDocument(), compressorList, serverApi); return new InternalStreamConnection( clusterConnectionMode, authenticator, isMonitoringConnection, serverId, connectionGenerationSupplier, diff --git a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedCluster.java b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedCluster.java index ba47236cf4f..2b9a0e8ddd4 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedCluster.java @@ -76,6 +76,7 @@ final class LoadBalancedCluster implements Cluster { private final ClusterId clusterId; private final ClusterSettings settings; private final ClusterClock clusterClock = new ClusterClock(); + private final ClientMetadata clientMetadata; private final ClusterListener clusterListener; private ClusterDescription description; @Nullable @@ -91,6 +92,7 @@ final class LoadBalancedCluster implements Cluster { private final Condition condition = lock.newCondition(); LoadBalancedCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata, final DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory) { assertTrue(settings.getMode() == ClusterConnectionMode.LOAD_BALANCED); LOGGER.info(format("Cluster created with id %s and settings %s", clusterId, settings.getShortDescription())); @@ -100,6 +102,7 @@ final class LoadBalancedCluster implements Cluster { this.clusterListener = singleClusterListener(settings); this.description = new ClusterDescription(settings.getMode(), ClusterType.UNKNOWN, emptyList(), settings, serverFactory.getSettings()); + this.clientMetadata = clientMetadata; if (settings.getSrvHost() == null) { dnsSrvRecordMonitor = null; @@ -204,6 +207,11 @@ public ClusterClock getClock() { return clusterClock; } + @Override + public ClientMetadata getClientMetadata() { + return clientMetadata; + } + @Override public ServerTuple selectServer(final ServerSelector serverSelector, final OperationContext operationContext) { isTrue("open", !isClosed()); diff --git a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java index bcd86fa5205..296240cf39f 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/LoadBalancedClusterableServerFactory.java @@ -19,7 +19,6 @@ import com.mongodb.LoggerSettings; import com.mongodb.MongoCompressor; import com.mongodb.MongoCredential; -import com.mongodb.MongoDriverInformation; import com.mongodb.ServerAddress; import com.mongodb.ServerApi; import com.mongodb.annotations.ThreadSafe; @@ -47,8 +46,6 @@ public class LoadBalancedClusterableServerFactory implements ClusterableServerFa private final MongoCredentialWithCache credential; private final LoggerSettings loggerSettings; private final CommandListener commandListener; - private final String applicationName; - private final MongoDriverInformation mongoDriverInformation; private final List compressorList; private final ServerApi serverApi; private final InternalOperationContextFactory operationContextFactory; @@ -59,7 +56,6 @@ public LoadBalancedClusterableServerFactory(final ServerSettings serverSettings, final StreamFactory streamFactory, @Nullable final MongoCredential credential, final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, - @Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation, final List compressorList, @Nullable final ServerApi serverApi, final InternalOperationContextFactory operationContextFactory) { this.serverSettings = serverSettings; @@ -69,8 +65,6 @@ public LoadBalancedClusterableServerFactory(final ServerSettings serverSettings, this.credential = credential == null ? null : new MongoCredentialWithCache(credential); this.loggerSettings = loggerSettings; this.commandListener = commandListener; - this.applicationName = applicationName; - this.mongoDriverInformation = mongoDriverInformation; this.compressorList = compressorList; this.serverApi = serverApi; this.operationContextFactory = operationContextFactory; @@ -79,8 +73,8 @@ public LoadBalancedClusterableServerFactory(final ServerSettings serverSettings, @Override public ClusterableServer create(final Cluster cluster, final ServerAddress serverAddress) { ConnectionPool connectionPool = new DefaultConnectionPool(new ServerId(cluster.getClusterId(), serverAddress), - new InternalStreamConnectionFactory(ClusterConnectionMode.LOAD_BALANCED, streamFactory, credential, applicationName, - mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi), + new InternalStreamConnectionFactory(ClusterConnectionMode.LOAD_BALANCED, streamFactory, credential, cluster.getClientMetadata(), + compressorList, loggerSettings, commandListener, serverApi), connectionPoolSettings, internalConnectionPoolSettings, EmptyProvider.instance(), operationContextFactory); connectionPool.ready(); diff --git a/driver-core/src/main/com/mongodb/internal/connection/MultiServerCluster.java b/driver-core/src/main/com/mongodb/internal/connection/MultiServerCluster.java index 186fe12dd61..55a11a10228 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/MultiServerCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/MultiServerCluster.java @@ -26,8 +26,9 @@ */ public final class MultiServerCluster extends AbstractMultiServerCluster { public MultiServerCluster(final ClusterId clusterId, final ClusterSettings settings, - final ClusterableServerFactory serverFactory) { - super(clusterId, settings, serverFactory); + final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata) { + super(clusterId, settings, serverFactory, clientMetadata); isTrue("srvHost is null", settings.getSrvHost() == null); initialize(settings.getHosts()); } diff --git a/driver-core/src/main/com/mongodb/internal/connection/SingleServerCluster.java b/driver-core/src/main/com/mongodb/internal/connection/SingleServerCluster.java index daeb67be54d..c21205559ee 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/SingleServerCluster.java +++ b/driver-core/src/main/com/mongodb/internal/connection/SingleServerCluster.java @@ -49,8 +49,9 @@ public final class SingleServerCluster extends BaseCluster { private final AtomicReference server; - public SingleServerCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory) { - super(clusterId, settings, serverFactory); + public SingleServerCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory, + final ClientMetadata clientMetadata) { + super(clusterId, settings, serverFactory, clientMetadata); isTrue("one server in a direct cluster", settings.getHosts().size() == 1); isTrue("connection mode is single", settings.getMode() == ClusterConnectionMode.SINGLE); diff --git a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java index f0004cd9e03..b61e2b2f117 100644 --- a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java +++ b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java @@ -50,6 +50,7 @@ import com.mongodb.internal.binding.SingleConnectionBinding; import com.mongodb.internal.connection.AsyncConnection; import com.mongodb.internal.connection.AsynchronousSocketChannelStreamFactory; +import com.mongodb.internal.connection.ClientMetadata; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.connection.DefaultClusterFactory; import com.mongodb.internal.connection.DefaultInetAddressResolver; @@ -127,6 +128,7 @@ public final class ClusterFixture { private static final int COMMAND_NOT_FOUND_ERROR_CODE = 59; public static final long TIMEOUT = 120L; public static final Duration TIMEOUT_DURATION = Duration.ofSeconds(TIMEOUT); + public static final ClientMetadata CLIENT_METADATA = new ClientMetadata("test", MongoDriverInformation.builder().build()); public static final TimeoutSettings TIMEOUT_SETTINGS = new TimeoutSettings(30_000, 10_000, 0, null, SECONDS.toMillis(5)); public static final TimeoutSettings TIMEOUT_SETTINGS_WITH_TIMEOUT = TIMEOUT_SETTINGS.withTimeout(TIMEOUT, SECONDS); diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java index 3adafc3a945..d0f3bff1ef9 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java @@ -43,7 +43,9 @@ import static com.mongodb.client.WithWrapper.withWrapper; import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.connection.ClientMetadataHelper.getOperatingSystemType; +import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMedataDocument; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -318,6 +320,43 @@ public void testCreateClientMetadataDocument(@Nullable final String appName, fin createClientMetadataDocument(appName, driverInformation)); } + + @Test + void testUpdateClientMetadataDocument() { + //given + MongoDriverInformation initialDriverInformation = MongoDriverInformation.builder() + .driverName("mongo-spark") + .driverVersion("2.0.0") + .driverPlatform("Scala 2.10 / Spark 2.0.0") + .build(); + + BsonDocument initialClientMetadataDocument = createClientMetadataDocument(null, initialDriverInformation); + assertEquals( + createExpectedClientMetadataDocument(null, initialDriverInformation), + initialClientMetadataDocument); + + MongoDriverInformation metadataToAppend = MongoDriverInformation.builder() + .driverVersion("2.0.0") + .driverName("Framework") + .driverPlatform("JDK 99") + .build(); + + MongoDriverInformation expectedUpdatedMetadata = MongoDriverInformation.builder(metadataToAppend) + .driverName("mongo-spark") + .driverVersion("2.0.0") + .driverPlatform("Scala 2.10 / Spark 2.0.0") + .build(); + + //when + BsonDocument updatedClientMetadata = updateClientMedataDocument(initialClientMetadataDocument, metadataToAppend); + + //then + assertEquals( + createExpectedClientMetadataDocument(null, expectedUpdatedMetadata), + updatedClientMetadata); + assertNotEquals(updatedClientMetadata, initialClientMetadataDocument); + } + @ParameterizedTest @CsvSource({ "unknown, unknown", diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java index 6ab01fdfc8a..b95b9c96894 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/PlainAuthenticatorTest.java @@ -32,6 +32,7 @@ import java.util.Collections; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static com.mongodb.ClusterFixture.OPERATION_CONTEXT; import static com.mongodb.ClusterFixture.getClusterConnectionMode; import static com.mongodb.ClusterFixture.getServerApi; @@ -52,8 +53,8 @@ public void setUp() { userName = System.getProperty("org.mongodb.test.userName"); source = System.getProperty("org.mongod.test.source"); password = System.getProperty("org.mongodb.test.password"); - internalConnection = new InternalStreamConnectionFactory(ClusterConnectionMode.SINGLE, streamFactory, null, null, - null, Collections.emptyList(), LoggerSettings.builder().build(), null, getServerApi() + internalConnection = new InternalStreamConnectionFactory(ClusterConnectionMode.SINGLE, streamFactory, null, CLIENT_METADATA, + Collections.emptyList(), LoggerSettings.builder().build(), null, getServerApi() ).create(new ServerId(new ClusterId(), new ServerAddress(host))); connectionDescription = new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress())); diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java index d66bcff46e3..62fa6c27032 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java @@ -36,6 +36,7 @@ import java.util.Collections; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static com.mongodb.ClusterFixture.OPERATION_CONTEXT; import static com.mongodb.ClusterFixture.OPERATION_CONTEXT_FACTORY; import static com.mongodb.ClusterFixture.getCredential; @@ -67,8 +68,8 @@ private void setUpCluster(final ServerAddress serverAddress) { new DefaultClusterableServerFactory(ServerSettings.builder().build(), ConnectionPoolSettings.builder().maxSize(1).build(), InternalConnectionPoolSettings.builder().build(), OPERATION_CONTEXT_FACTORY, streamFactory, OPERATION_CONTEXT_FACTORY, streamFactory, getCredential(), - LoggerSettings.builder().build(), null, null, null, - Collections.emptyList(), getServerApi(), false)); + LoggerSettings.builder().build(), null, + Collections.emptyList(), getServerApi(), false), CLIENT_METADATA); } @After diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java index 0cf8deb479d..92e224df835 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractConnectionPoolTest.java @@ -188,10 +188,11 @@ public void setUp() { pool = new ConnectionIdAdjustingConnectionPool(new DefaultConnectionPool(serverId, new InternalStreamConnectionFactory( connectionMode, - createStreamFactory(SocketSettings.builder().build(), ClusterFixture.getSslSettings()), + createStreamFactory(SocketSettings.builder().build(), + ClusterFixture.getSslSettings()), ClusterFixture.getCredentialWithCache(), - poolOptions.getString("appName", new BsonString(fileName + ": " + description)).getValue(), - MongoDriverInformation.builder().build(), + new ClientMetadata(poolOptions.getString("appName", new BsonString(fileName + ": " + description)).getValue(), + MongoDriverInformation.builder().build()), Collections.emptyList(), LoggerSettings.builder().build(), new TestCommandListener(), diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractServerDiscoveryAndMonitoringTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractServerDiscoveryAndMonitoringTest.java index 514f5bde383..fa16462dabb 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractServerDiscoveryAndMonitoringTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/AbstractServerDiscoveryAndMonitoringTest.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static com.mongodb.ClusterFixture.OPERATION_CONTEXT; import static com.mongodb.ClusterFixture.TIMEOUT_SETTINGS; import static com.mongodb.connection.ServerConnectionState.CONNECTING; @@ -187,11 +188,11 @@ protected void init(final ServerListenerFactory serverListenerFactory, final Clu : ClusterSettings.builder(settings).addClusterListener(clusterListener).build(); if (settings.getMode() == ClusterConnectionMode.SINGLE) { - cluster = new SingleServerCluster(clusterId, clusterSettings, factory); + cluster = new SingleServerCluster(clusterId, clusterSettings, factory, CLIENT_METADATA); } else if (settings.getMode() == ClusterConnectionMode.MULTIPLE) { - cluster = new MultiServerCluster(clusterId, clusterSettings, factory); + cluster = new MultiServerCluster(clusterId, clusterSettings, factory, CLIENT_METADATA); } else { - cluster = new LoadBalancedCluster(clusterId, clusterSettings, factory, null); + cluster = new LoadBalancedCluster(clusterId, clusterSettings, factory, CLIENT_METADATA, null); } } diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/InitialDnsSeedListDiscoveryProseTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/InitialDnsSeedListDiscoveryProseTest.java index 27ed86e7b63..d49f67a1e38 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/InitialDnsSeedListDiscoveryProseTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/InitialDnsSeedListDiscoveryProseTest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static java.util.Collections.singletonList; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -117,6 +118,7 @@ private void doTest(final String srvHost, final String resolvedHost, final boole cluster = new DnsMultiServerCluster(clusterId, settingsBuilder.build(), serverFactory, + CLIENT_METADATA, dnsSrvRecordMonitorFactory); ClusterFixture.sleep(100); diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/LoadBalancedClusterTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/LoadBalancedClusterTest.java index ad447f3da65..7366a03b584 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/LoadBalancedClusterTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/LoadBalancedClusterTest.java @@ -51,6 +51,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static com.mongodb.ClusterFixture.OPERATION_CONTEXT; import static com.mongodb.ClusterFixture.TIMEOUT_SETTINGS; import static com.mongodb.ClusterFixture.createOperationContext; @@ -91,7 +92,8 @@ public void shouldSelectServerWhenThereIsNoSRVLookup() { .build(); ClusterableServerFactory serverFactory = mockServerFactory(serverAddress, expectedServer); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, mock(DnsSrvRecordMonitorFactory.class)); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, + mock(DnsSrvRecordMonitorFactory.class)); // when ServerTuple serverTuple = cluster.selectServer(mock(ServerSelector.class), OPERATION_CONTEXT); @@ -126,7 +128,7 @@ public void shouldSelectServerWhenThereIsSRVLookup() { when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); // when ServerTuple serverTuple = cluster.selectServer(mock(ServerSelector.class), OPERATION_CONTEXT); @@ -153,7 +155,7 @@ public void shouldSelectServerAsynchronouslyWhenThereIsSRVLookup() { when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); // when FutureResultCallback callback = new FutureResultCallback<>(); @@ -180,7 +182,7 @@ public void shouldFailSelectServerWhenThereIsSRVMisconfiguration() { invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)) .hosts(Arrays.asList(new ServerAddress("host1"), new ServerAddress("host2")))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); MongoClientException exception = assertThrows(MongoClientException.class, () -> cluster.selectServer(mock(ServerSelector.class), OPERATION_CONTEXT)); @@ -204,7 +206,7 @@ public void shouldFailSelectServerAsynchronouslyWhenThereIsSRVMisconfiguration() invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)) .hosts(Arrays.asList(new ServerAddress("host1"), new ServerAddress("host2")))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); FutureResultCallback callback = new FutureResultCallback<>(); cluster.selectServerAsync(mock(ServerSelector.class), OPERATION_CONTEXT, callback); @@ -232,7 +234,7 @@ public void shouldTimeoutSelectServerWhenThereIsSRVLookup() { when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)).sleepTime(Duration.ofHours(1))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); MongoTimeoutException exception = assertThrows(MongoTimeoutException.class, () -> cluster.selectServer(mock(ServerSelector.class), createOperationContext(TIMEOUT_SETTINGS.withServerSelectionTimeoutMS(5)))); @@ -257,7 +259,7 @@ public void shouldTimeoutSelectServerWhenThereIsSRVLookupAndTimeoutMsIsSet() { when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)).sleepTime(Duration.ofHours(1))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); //when & then MongoOperationTimeoutException exception = assertThrows(MongoOperationTimeoutException.class, () -> cluster.selectServer(mock(ServerSelector.class), @@ -284,7 +286,7 @@ public void shouldTimeoutSelectServerWhenThereIsSRVLookupException() { invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)) .sleepTime(Duration.ofMillis(1)) .exception(new MongoConfigurationException("Unable to resolve SRV record"))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); MongoTimeoutException exception = assertThrows(MongoTimeoutException.class, () -> cluster.selectServer(mock(ServerSelector.class), createOperationContext(TIMEOUT_SETTINGS.withServerSelectionTimeoutMS(10)))); @@ -312,7 +314,7 @@ public void shouldTimeoutSelectServerAsynchronouslyWhenThereIsSRVLookup() { when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)).sleepTime(Duration.ofHours(1))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); FutureResultCallback callback = new FutureResultCallback<>(); cluster.selectServerAsync(mock(ServerSelector.class), @@ -341,7 +343,7 @@ public void shouldTimeoutSelectServerAsynchronouslyWhenThereIsSRVLookupException invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)) .sleepTime(Duration.ofMillis(1)) .exception(new MongoConfigurationException("Unable to resolve SRV record"))); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); FutureResultCallback callback = new FutureResultCallback<>(); cluster.selectServerAsync(mock(ServerSelector.class), @@ -362,7 +364,7 @@ void shouldNotInitServerAfterClosing() { when(srvRecordMonitorFactory.create(any(), eq(clusterSettings.getSrvServiceName()), any(DnsSrvRecordInitializer.class))).thenReturn(mock(DnsSrvRecordMonitor.class)); ArgumentCaptor serverInitializerCaptor = ArgumentCaptor.forClass(DnsSrvRecordInitializer.class); // create `cluster` and capture its `DnsSrvRecordInitializer` (server initializer) - LoadBalancedCluster cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, srvRecordMonitorFactory); + LoadBalancedCluster cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, srvRecordMonitorFactory); verify(srvRecordMonitorFactory, times(1)).create(any(), eq(clusterSettings.getSrvServiceName()), serverInitializerCaptor.capture()); // close `cluster`, call `DnsSrvRecordInitializer.initialize` and check that it does not result in creating a `ClusterableServer` cluster.close(); @@ -379,7 +381,7 @@ void shouldCloseServerWhenClosing() { when(serverFactory.create(any(), any())).thenReturn(server); // create `cluster` and check that it creates a `ClusterableServer` LoadBalancedCluster cluster = new LoadBalancedCluster(new ClusterId(), - ClusterSettings.builder().mode(ClusterConnectionMode.LOAD_BALANCED).build(), serverFactory, + ClusterSettings.builder().mode(ClusterConnectionMode.LOAD_BALANCED).build(), serverFactory, CLIENT_METADATA, mock(DnsSrvRecordMonitorFactory.class)); verify(serverFactory, times(1)).create(any(), any()); // close `cluster` and check that it closes `server` @@ -405,7 +407,7 @@ public void synchronousConcurrentTest() throws InterruptedException, ExecutionEx DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = mock(DnsSrvRecordMonitorFactory.class); when(dnsSrvRecordMonitorFactory.create(eq(srvHostName), eq(clusterSettings.getSrvServiceName()), any())).thenAnswer( invocation -> new TestDnsSrvRecordMonitor(invocation.getArgument(2)).sleepTime(srvResolutionTime)); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); int numThreads = 100; ExecutorService executorService = Executors.newFixedThreadPool(numThreads); @@ -461,7 +463,7 @@ public void asynchronousConcurrentTest() throws InterruptedException, ExecutionE dnsSrvRecordMonitorReference.set(dnsSrvRecordMonitor); return dnsSrvRecordMonitor; }); - cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, dnsSrvRecordMonitorFactory); + cluster = new LoadBalancedCluster(new ClusterId(), clusterSettings, serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); int numThreads = 10; List>> callbacksList = new ArrayList<>(numThreads); diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/SrvPollingProseTests.java b/driver-core/src/test/unit/com/mongodb/internal/connection/SrvPollingProseTests.java index a0f08a82360..51cc4884f02 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/SrvPollingProseTests.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/SrvPollingProseTests.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static com.mongodb.ClusterFixture.CLIENT_METADATA; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -198,7 +199,7 @@ private void initCluster(final TestDnsResolver dnsResolver, @Nullable final Inte invocation.getArgument(2), clusterId, dnsResolver); return dnsSrvRecordMonitor; }); - cluster = new DnsMultiServerCluster(clusterId, settingsBuilder.srvMaxHosts(srvMaxHosts).build(), serverFactory, + cluster = new DnsMultiServerCluster(clusterId, settingsBuilder.srvMaxHosts(srvMaxHosts).build(), serverFactory, CLIENT_METADATA, dnsSrvRecordMonitorFactory); try { Thread.sleep(100); // racy diff --git a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt index bfa48ef1e1c..139869f5929 100644 --- a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt @@ -15,12 +15,17 @@ */ package com.mongodb.kotlin.client.coroutine.syncadapter -import com.mongodb.client.MongoClient as JMongoClient +import com.mongodb.MongoDriverInformation import com.mongodb.connection.ClusterDescription import com.mongodb.kotlin.client.coroutine.MongoClient +import com.mongodb.client.MongoClient as JMongoClient internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoCluster(wrapped), JMongoClient { override fun close(): Unit = wrapped.close() override fun getClusterDescription(): ClusterDescription = wrapped.getClusterDescription() + + override fun updateClientMetadata(mongoDriverInformation: MongoDriverInformation) { + // TODO + } } diff --git a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt index 16660562a33..9ce29cd5d81 100644 --- a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -15,12 +15,16 @@ */ package com.mongodb.kotlin.client.syncadapter -import com.mongodb.client.MongoClient as JMongoClient +import com.mongodb.MongoDriverInformation import com.mongodb.connection.ClusterDescription import com.mongodb.kotlin.client.MongoClient +import com.mongodb.client.MongoClient as JMongoClient internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoCluster(wrapped), JMongoClient { override fun close(): Unit = wrapped.close() override fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription + override fun updateClientMetadata(mongoDriverInformation: MongoDriverInformation) { + TODO("Not yet implemented") + } } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java index 3f2265cb795..3ec71b0f444 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java @@ -18,6 +18,7 @@ import com.mongodb.ClientBulkWriteException; import com.mongodb.ClientSessionOptions; +import com.mongodb.MongoDriverInformation; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; @@ -29,8 +30,8 @@ import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoIterable; import com.mongodb.client.model.bulk.ClientBulkWriteOptions; -import com.mongodb.client.model.bulk.ClientNamespacedWriteModel; import com.mongodb.client.model.bulk.ClientBulkWriteResult; +import com.mongodb.client.model.bulk.ClientNamespacedWriteModel; import com.mongodb.connection.ClusterDescription; import com.mongodb.reactivestreams.client.internal.BatchCursor; import org.bson.Document; @@ -311,4 +312,9 @@ public ClusterDescription getClusterDescription() { return wrapped.getClusterDescription(); } + @Override + public void updateClientMetadata(final MongoDriverInformation mongoDriverInformation) { + //TODO + } + } diff --git a/driver-sync/src/main/com/mongodb/client/MongoClient.java b/driver-sync/src/main/com/mongodb/client/MongoClient.java index 14519e2413a..4aa84612020 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoClient.java +++ b/driver-sync/src/main/com/mongodb/client/MongoClient.java @@ -16,6 +16,7 @@ package com.mongodb.client; +import com.mongodb.MongoDriverInformation; import com.mongodb.annotations.Immutable; import com.mongodb.connection.ClusterDescription; import com.mongodb.connection.ClusterSettings; @@ -61,4 +62,6 @@ public interface MongoClient extends MongoCluster, Closeable { * @since 3.11 */ ClusterDescription getClusterDescription(); + + void updateClientMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-sync/src/main/com/mongodb/client/MongoCluster.java b/driver-sync/src/main/com/mongodb/client/MongoCluster.java index f097f71288f..8ffc54612b0 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoCluster.java +++ b/driver-sync/src/main/com/mongodb/client/MongoCluster.java @@ -28,10 +28,10 @@ import com.mongodb.annotations.Immutable; import com.mongodb.annotations.Reason; import com.mongodb.client.model.bulk.ClientBulkWriteOptions; +import com.mongodb.client.model.bulk.ClientBulkWriteResult; import com.mongodb.client.model.bulk.ClientNamespacedDeleteManyModel; import com.mongodb.client.model.bulk.ClientNamespacedUpdateManyModel; import com.mongodb.client.model.bulk.ClientNamespacedWriteModel; -import com.mongodb.client.model.bulk.ClientBulkWriteResult; import com.mongodb.lang.Nullable; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; diff --git a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java index cf9ca2a3b7d..17f43d18a27 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -135,6 +135,11 @@ public ClusterDescription getClusterDescription() { return delegate.getCluster().getCurrentDescription(); } + @Override + public void updateClientMetadata(final MongoDriverInformation mongoDriverInformation) { + delegate.getCluster().getClientMetadata().updateClientMetadataBsonDocument(mongoDriverInformation); + } + @Override public CodecRegistry getCodecRegistry() { return delegate.getCodecRegistry(); From 889d5c81f0156b19344c557f90dc7dd59315dc77 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 7 May 2025 21:44:32 -0700 Subject: [PATCH 02/26] Add prose tests. JAVA-5854 --- .../conventions/testing-base.gradle.kts | 4 + .../InternalStreamConnectionFactory.java | 1 - .../coroutine/syncadapter/SyncMongoClient.kt | 4 +- .../client/syncadapter/SyncMongoClient.kt | 4 +- .../reactivestreams/client/MongoClient.java | 4 + .../client/internal/MongoClientImpl.java | 5 + .../AbstractClientMetadataProseTest.java | 34 +++ .../client/syncadapter/SyncMongoClient.java | 5 +- .../scala/syncadapter/SyncMongoClient.scala | 13 +- .../main/com/mongodb/client/MongoClient.java | 2 +- .../client/internal/MongoClientImpl.java | 4 +- .../AbstractClientMetadataProseTest.java | 195 ++++++++++++++++++ .../client/ClientMetadataProseTest.java | 30 +++ 13 files changed, 285 insertions(+), 20 deletions(-) create mode 100644 driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/AbstractClientMetadataProseTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/ClientMetadataProseTest.java diff --git a/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts b/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts index 8aa6d25a5fd..da4a9b10866 100644 --- a/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts +++ b/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts @@ -105,3 +105,7 @@ testlogger { showFailedStandardStreams = true logLevel = LogLevel.LIFECYCLE } + +dependencies { + testImplementation(libs.assertj) +} diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java index c4bf015ffe3..e75a2ed479f 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java @@ -33,7 +33,6 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { private final ClusterConnectionMode clusterConnectionMode; private final boolean isMonitoringConnection; private final StreamFactory streamFactory; - //TODO UPDATE private final ClientMetadata clientMetadata; private final List compressorList; private final LoggerSettings loggerSettings; diff --git a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt index 139869f5929..6d5f93a465f 100644 --- a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt @@ -25,7 +25,7 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun getClusterDescription(): ClusterDescription = wrapped.getClusterDescription() - override fun updateClientMetadata(mongoDriverInformation: MongoDriverInformation) { - // TODO + override fun updateMetadata(mongoDriverInformation: MongoDriverInformation) { + throw UnsupportedOperationException("TODO-JAVA-5871") } } diff --git a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt index 9ce29cd5d81..9493ba1221e 100644 --- a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -24,7 +24,7 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun close(): Unit = wrapped.close() override fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription - override fun updateClientMetadata(mongoDriverInformation: MongoDriverInformation) { - TODO("Not yet implemented") + override fun updateMetadata(mongoDriverInformation: MongoDriverInformation) { + throw UnsupportedOperationException("TODO-JAVA-5871") } } diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java index 061fd3c8bed..ffcaad19f35 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java @@ -16,6 +16,7 @@ package com.mongodb.reactivestreams.client; +import com.mongodb.MongoDriverInformation; import com.mongodb.annotations.Immutable; import com.mongodb.connection.ClusterDescription; import com.mongodb.connection.ClusterSettings; @@ -58,4 +59,7 @@ public interface MongoClient extends MongoCluster, Closeable { * @since 4.1 */ ClusterDescription getClusterDescription(); + + + void updateMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java index 3d4822eb7e3..96cc6d819d6 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java @@ -325,4 +325,9 @@ public MongoDatabase getDatabase(final String name) { public ClusterDescription getClusterDescription() { return getCluster().getCurrentDescription(); } + + @Override + public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { + getCluster().getClientMetadata().append(mongoDriverInformation); + } } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/AbstractClientMetadataProseTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/AbstractClientMetadataProseTest.java new file mode 100644 index 00000000000..60343711ba9 --- /dev/null +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/AbstractClientMetadataProseTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.reactivestreams.client; + +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoDriverInformation; +import com.mongodb.client.AbstractClientMetadataProseTest; +import com.mongodb.client.MongoClient; +import com.mongodb.lang.Nullable; +import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient; + +/** + * See spec + */ +class ClientMetadataProseTest extends AbstractClientMetadataProseTest { + + protected MongoClient createMongoClient(@Nullable final MongoDriverInformation mongoDriverInformation, final MongoClientSettings mongoClientSettings) { + return new SyncMongoClient(MongoClients.create(mongoClientSettings, mongoDriverInformation)); + } +} diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java index 3ec71b0f444..73a4bde4545 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java @@ -313,8 +313,7 @@ public ClusterDescription getClusterDescription() { } @Override - public void updateClientMetadata(final MongoDriverInformation mongoDriverInformation) { - //TODO + public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { + wrapped.updateMetadata(mongoDriverInformation); } - } diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala index 4daa6d94ef1..7002647196d 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala @@ -1,15 +1,8 @@ package org.mongodb.scala.syncadapter -import com.mongodb.ClientSessionOptions -import com.mongodb.client.{ ClientSession, MongoClient => JMongoClient, MongoDatabase => JMongoDatabase } -import org.bson.Document -import org.bson.conversions.Bson +import com.mongodb.MongoDriverInformation +import com.mongodb.client.{MongoClient => JMongoClient} import org.mongodb.scala.MongoClient -import org.mongodb.scala.bson.DefaultHelper.DefaultsTo - -import scala.collection.JavaConverters._ -import scala.concurrent.Await -import scala.reflect.ClassTag case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrapped) with JMongoClient { @@ -17,4 +10,6 @@ case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrappe override def getClusterDescription = throw new UnsupportedOperationException + override def updateMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + throw new UnsupportedOperationException("TODO-JAVA-5871") } diff --git a/driver-sync/src/main/com/mongodb/client/MongoClient.java b/driver-sync/src/main/com/mongodb/client/MongoClient.java index 4aa84612020..4b2a9114f52 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoClient.java +++ b/driver-sync/src/main/com/mongodb/client/MongoClient.java @@ -63,5 +63,5 @@ public interface MongoClient extends MongoCluster, Closeable { */ ClusterDescription getClusterDescription(); - void updateClientMetadata(MongoDriverInformation mongoDriverInformation); + void updateMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java index 17f43d18a27..c1c5288bf9e 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -136,8 +136,8 @@ public ClusterDescription getClusterDescription() { } @Override - public void updateClientMetadata(final MongoDriverInformation mongoDriverInformation) { - delegate.getCluster().getClientMetadata().updateClientMetadataBsonDocument(mongoDriverInformation); + public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { + delegate.getCluster().getClientMetadata().append(mongoDriverInformation); } @Override diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java new file mode 100644 index 00000000000..7cb3ca07de4 --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -0,0 +1,195 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.client; + +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoDriverInformation; +import com.mongodb.event.CommandStartedEvent; +import com.mongodb.event.ConnectionClosedEvent; +import com.mongodb.internal.connection.InternalStreamConnection; +import com.mongodb.internal.connection.TestCommandListener; +import com.mongodb.internal.connection.TestConnectionPoolListener; +import com.mongodb.lang.Nullable; +import org.bson.BsonDocument; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static com.mongodb.ClusterFixture.sleep; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * See spec + */ +public abstract class AbstractClientMetadataProseTest { + + protected TestCommandListener commandListener; + protected TestConnectionPoolListener connectionPoolListener; + + protected abstract MongoClient createMongoClient(@Nullable final MongoDriverInformation driverInformation, + final MongoClientSettings mongoClientSettings); + + @BeforeEach + public void setUp() { + commandListener = new TestCommandListener(); + connectionPoolListener = new TestConnectionPoolListener(); + InternalStreamConnection.setRecordEverything(true); + } + + @Test + void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { + //given + MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() + .driverName("library") + .driverVersion("1.2") + .driverPlatform("Library Platform") + .build(); + + try (MongoClient mongoClient = createMongoClient(initialWrappingLibraryDriverInformation, getMongoClientSettingsBuilder() + .applyToConnectionPoolSettings(builder -> + builder.maxConnectionIdleTime(1, TimeUnit.MILLISECONDS)) + .build())) { + + //TODO change get() to orElseThrow + BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument driverInformation = clientMetadata.getDocument("driver"); + String generatedDriverName = driverInformation.get("name").asString().getValue(); + String generatedVersionName = driverInformation.get("version").asString().getValue(); + String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + + //when + sleep(5); // wait for connection to become idle + mongoClient.updateMetadata(MongoDriverInformation.builder() + .driverVersion("1.0") + .driverName("Framework") + .driverPlatform("Framework Platform") + .build()); + + //then + //TODO change get() to orElseThrow + clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + driverInformation = clientMetadata.getDocument("driver"); + + assertThat(driverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); + assertThat(driverInformation.getString("version").getValue()).isEqualTo(generatedVersionName + "|1.0"); + assertThat(clientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName + "|Framework Platform"); + } + } + + @Test + void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { + //given + try (MongoClient mongoClient = createMongoClient(null, getMongoClientSettingsBuilder() + .applyToConnectionPoolSettings(builder -> + builder.maxConnectionIdleTime(1, TimeUnit.MILLISECONDS)) + .build())) { + + //TODO change get() to orElseThrow + BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + + BsonDocument generatedDriverInformation = clientMetadata.getDocument("driver"); + String generatedDriverName = generatedDriverInformation.get("name").asString().getValue(); + String generatedVersionName = generatedDriverInformation.get("version").asString().getValue(); + String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + + //when + sleep(5); // wait for connection to become idle + mongoClient.updateMetadata(MongoDriverInformation.builder() + .driverVersion("1.0") + .driverName("Framework") + .driverPlatform("Framework Platform") + .build()); + + //then + //TODO change get() to orElseThrow + clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + generatedDriverInformation = clientMetadata.getDocument("driver"); + + assertThat(generatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); + assertThat(generatedDriverInformation.getString("version").getValue()).endsWith(generatedVersionName + "|1.0"); + assertThat(clientMetadata.getString("platform").getValue()).endsWith(generatedPlatformName + "|Framework Platform"); + } + } + + @Test + void shouldNotCloseExistingConnectionsToUpdateMetadata() { + //given + MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() + .driverName("library") + .driverVersion("1.2") + .driverPlatform("Library Platform") + .build(); + + try (MongoClient mongoClient = createMongoClient(initialWrappingLibraryDriverInformation, getMongoClientSettingsBuilder() + .build())) { + + //TODO change get() to orElseThrow + BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument driverInformation = clientMetadata.getDocument("driver"); + String generatedDriverName = driverInformation.get("name").asString().getValue(); + String generatedVersionName = driverInformation.get("version").asString().getValue(); + String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + + //when + mongoClient.updateMetadata(initialWrappingLibraryDriverInformation); + + //then + assertThat(executePingAndCaptureMetadataHandshake(mongoClient)).isEmpty(); + driverInformation = clientMetadata.getDocument("driver"); + + assertThat(driverInformation.getString("name").getValue()).isEqualTo(generatedDriverName); + assertThat(driverInformation.getString("version").getValue()).isEqualTo(generatedVersionName); + assertThat(clientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName); + + assertFalse(connectionPoolListener.getEvents().stream().anyMatch(ConnectionClosedEvent.class::isInstance), + "Expected no connection closed events"); + } + } + + private Optional executePingAndCaptureMetadataHandshake(final MongoClient mongoClient) { + commandListener.reset(); + mongoClient.getDatabase("admin") + .runCommand(BsonDocument.parse("{ping: 1}")); + + List commandStartedEvents = commandListener.getCommandStartedEvents("isMaster"); + + if (commandStartedEvents.isEmpty()) { + return Optional.empty(); + } + CommandStartedEvent event = commandStartedEvents.get(0); + BsonDocument helloCommand = event.getCommand(); + return Optional.of(helloCommand.getDocument("client")); + } + + protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { + return MongoClientSettings.builder() + .addCommandListener(commandListener) + .applyToConnectionPoolSettings(builder -> + builder.addConnectionPoolListener(connectionPoolListener)); + } + + @AfterEach + public void tearDown() { + InternalStreamConnection.setRecordEverything(false); + } +} + diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataProseTest.java new file mode 100644 index 00000000000..f457eb350fe --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataProseTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.client; + +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoDriverInformation; +import com.mongodb.lang.Nullable; + +public class ClientMetadataProseTest extends AbstractClientMetadataProseTest { + + @Override + protected MongoClient createMongoClient(@Nullable final MongoDriverInformation mongoDriverInformation, + final MongoClientSettings mongoClientSettings) { + return MongoClients.create(mongoClientSettings, mongoDriverInformation); + } +} From 4b3065c9ac81f21267b4d819d3bc54807800a0ed Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 7 May 2025 22:18:00 -0700 Subject: [PATCH 03/26] Add prose tests. JAVA-5854 --- .../com/mongodb/client/AbstractClientMetadataProseTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 7cb3ca07de4..81c58e0f2ea 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -16,6 +16,7 @@ package com.mongodb.client; +import com.mongodb.ClusterFixture; import com.mongodb.MongoClientSettings; import com.mongodb.MongoDriverInformation; import com.mongodb.event.CommandStartedEvent; @@ -36,6 +37,7 @@ import static com.mongodb.ClusterFixture.sleep; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assumptions.assumeFalse; /** * See spec @@ -50,6 +52,8 @@ protected abstract MongoClient createMongoClient(@Nullable final MongoDriverInfo @BeforeEach public void setUp() { + assumeFalse(ClusterFixture.isLoadBalanced()); + commandListener = new TestCommandListener(); connectionPoolListener = new TestConnectionPoolListener(); InternalStreamConnection.setRecordEverything(true); From 2a684bf3cb0ffac04774afb5467a4d39e0ca5ff8 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 7 May 2025 22:36:53 -0700 Subject: [PATCH 04/26] Add ClientMetadata entity. JAVA-5854 --- .../internal/connection/ClientMetadata.java | 49 +++++++++++++++++++ .../InternalStreamConnectionFactory.java | 10 ++-- .../ClientMetadataHelperProseTest.java | 1 + 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java new file mode 100644 index 00000000000..318da52ba6e --- /dev/null +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -0,0 +1,49 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.internal.connection; + +import com.mongodb.MongoDriverInformation; +import com.mongodb.lang.Nullable; +import org.bson.BsonDocument; + +import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; +import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMedataDocument; + +/** + * Represents metadata of the current MongoClient. + * + *

This class is not part of the public API and may be removed or changed at any time

+ */ +public class ClientMetadata { + private volatile BsonDocument clientMetadataBsonDocument; + + public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { + this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); + } + + /** + * Returns mutable BsonDocument that represents the client metadata. + */ + public BsonDocument getBsonDocument() { + return clientMetadataBsonDocument; + } + + public void append(final MongoDriverInformation mongoDriverInformation) { + this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument, mongoDriverInformation); + } +} + diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java index e75a2ed479f..953c45a07f6 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java @@ -42,8 +42,8 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { private final MongoCredentialWithCache credential; InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode, - final StreamFactory streamFactory, - @Nullable final MongoCredentialWithCache credential, + final StreamFactory streamFactory, + @Nullable final MongoCredentialWithCache credential, final ClientMetadata clientMetadata, final List compressorList, final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) { @@ -52,8 +52,8 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { } InternalStreamConnectionFactory(final ClusterConnectionMode clusterConnectionMode, final boolean isMonitoringConnection, - final StreamFactory streamFactory, - @Nullable final MongoCredentialWithCache credential, + final StreamFactory streamFactory, + @Nullable final MongoCredentialWithCache credential, final ClientMetadata clientMetadata, final List compressorList, final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) { @@ -72,7 +72,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { public InternalConnection create(final ServerId serverId, final ConnectionGenerationSupplier connectionGenerationSupplier) { Authenticator authenticator = credential == null ? null : createAuthenticator(credential); InternalStreamConnectionInitializer connectionInitializer = new InternalStreamConnectionInitializer( - clusterConnectionMode, authenticator, clientMetadata.getClientMetadataBsonDocument(), compressorList, serverApi); + clusterConnectionMode, authenticator, clientMetadata.getBsonDocument(), compressorList, serverApi); return new InternalStreamConnection( clusterConnectionMode, authenticator, isMonitoringConnection, serverId, connectionGenerationSupplier, diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java index d0f3bff1ef9..3aedaee2848 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java @@ -341,6 +341,7 @@ void testUpdateClientMetadataDocument() { .driverPlatform("JDK 99") .build(); + //We pass metadataToAppend to a builder and prepend with initial driver information. MongoDriverInformation expectedUpdatedMetadata = MongoDriverInformation.builder(metadataToAppend) .driverName("mongo-spark") .driverVersion("2.0.0") From 28c18440289cb41179cd9f6f3382a11f507cf8f8 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 7 May 2025 23:54:20 -0700 Subject: [PATCH 05/26] Update tests. JAVA-5854 --- .../InternalStreamConnectionFactory.java | 5 +- .../CommandHelperSpecification.groovy | 3 +- .../ServerMonitorSpecification.groovy | 3 +- .../BaseClusterSpecification.groovy | 29 ++++----- .../DefaultServerSpecification.groovy | 3 +- .../DnsMultiServerClusterSpecification.groovy | 3 +- .../MultiServerClusterSpecification.groovy | 61 ++++++++++--------- .../SingleServerClusterSpecification.groovy | 17 +++--- .../mongodb/kotlin/client/MongoClientTest.kt | 17 ++++-- .../org/mongodb/scala/MongoClientSpec.scala | 11 ++-- .../AbstractClientMetadataProseTest.java | 8 +-- 11 files changed, 89 insertions(+), 71 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java index 953c45a07f6..252d62c35f8 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionFactory.java @@ -45,8 +45,9 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory { final StreamFactory streamFactory, @Nullable final MongoCredentialWithCache credential, final ClientMetadata clientMetadata, - final List compressorList, - final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, @Nullable final ServerApi serverApi) { + final List compressorList, + final LoggerSettings loggerSettings, @Nullable final CommandListener commandListener, + @Nullable final ServerApi serverApi) { this(clusterConnectionMode, false, streamFactory, credential, clientMetadata, compressorList, loggerSettings, commandListener, serverApi); } diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy index 085a5100198..83ce94f7075 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/CommandHelperSpecification.groovy @@ -29,6 +29,7 @@ import spock.lang.Specification import java.util.concurrent.CountDownLatch +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.LEGACY_HELLO import static com.mongodb.ClusterFixture.OPERATION_CONTEXT import static com.mongodb.ClusterFixture.getClusterConnectionMode @@ -44,7 +45,7 @@ class CommandHelperSpecification extends Specification { def setup() { connection = new InternalStreamConnectionFactory(ClusterConnectionMode.SINGLE, new NettyStreamFactory(SocketSettings.builder().build(), getSslSettings()), - getCredentialWithCache(), null, null, [], LoggerSettings.builder().build(), null, getServerApi()) + getCredentialWithCache(), CLIENT_METADATA, [], LoggerSettings.builder().build(), null, getServerApi()) .create(new ServerId(new ClusterId(), getPrimary())) connection.open(OPERATION_CONTEXT) } diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy index 266f4e88996..4cd36909238 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ServerMonitorSpecification.groovy @@ -34,6 +34,7 @@ import org.bson.types.ObjectId import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT_FACTORY import static com.mongodb.ClusterFixture.getClusterConnectionMode import static com.mongodb.ClusterFixture.getCredentialWithCache @@ -223,7 +224,7 @@ class ServerMonitorSpecification extends OperationFunctionalSpecification { serverMonitor = new DefaultServerMonitor(new ServerId(new ClusterId(), address), ServerSettings.builder().build(), new InternalStreamConnectionFactory(SINGLE, new SocketStreamFactory(new DefaultInetAddressResolver(), SocketSettings.builder().connectTimeout(500, TimeUnit.MILLISECONDS).build(), getSslSettings()), - getCredentialWithCache(), null, null, [], LoggerSettings.builder().build(), null, + getCredentialWithCache(), CLIENT_METADATA, [], LoggerSettings.builder().build(), null, getServerApi()), getClusterConnectionMode(), getServerApi(), false, SameObjectProvider.initialized(sdam), OPERATION_CONTEXT_FACTORY) diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/BaseClusterSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/BaseClusterSpecification.groovy index a509779d09f..56c500c6183 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/BaseClusterSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/BaseClusterSpecification.groovy @@ -36,11 +36,12 @@ import com.mongodb.internal.selector.ReadPreferenceServerSelector import com.mongodb.internal.selector.ServerAddressSelector import com.mongodb.internal.selector.WritableServerSelector import com.mongodb.internal.time.Timeout -import spock.lang.Specification import com.mongodb.spock.Slow +import spock.lang.Specification import java.util.concurrent.CountDownLatch +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT import static com.mongodb.ClusterFixture.TIMEOUT_SETTINGS import static com.mongodb.ClusterFixture.createOperationContext @@ -68,7 +69,7 @@ class BaseClusterSpecification extends Specification { .hosts([firstServer, secondServer, thirdServer]) .serverSelector(new ServerAddressSelector(firstServer)) .build() - def cluster = new BaseCluster(new ClusterId(), clusterSettings, factory) { + def cluster = new BaseCluster(new ClusterId(), clusterSettings, factory, CLIENT_METADATA) { @Override protected void connect() { } @@ -114,7 +115,7 @@ class BaseClusterSpecification extends Specification { .serverSelectionTimeout(1, SECONDS) .serverSelector(new ServerAddressSelector(firstServer)) .build() - def cluster = new MultiServerCluster(new ClusterId(), clusterSettings, factory) + def cluster = new MultiServerCluster(new ClusterId(), clusterSettings, factory, CLIENT_METADATA) expect: cluster.getSettings() == clusterSettings @@ -128,7 +129,7 @@ class BaseClusterSpecification extends Specification { .serverSelectionTimeout(1, SECONDS) .serverSelector(new ServerAddressSelector(firstServer)) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(secondServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(thirdServer, REPLICA_SET_PRIMARY, allServers) @@ -144,7 +145,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(secondServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(thirdServer, REPLICA_SET_PRIMARY, allServers) @@ -164,7 +165,7 @@ class BaseClusterSpecification extends Specification { .serverSelector(new ReadPreferenceServerSelector(ReadPreference.secondary())) .localThreshold(5, MILLISECONDS) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, 1, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(secondServer, 7, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(thirdServer, 1, REPLICA_SET_PRIMARY, allServers) @@ -182,7 +183,7 @@ class BaseClusterSpecification extends Specification { .hosts([firstServer, secondServer, thirdServer]) .localThreshold(5, MILLISECONDS) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, 1, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(secondServer, 7, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(thirdServer, 1, REPLICA_SET_PRIMARY, allServers) @@ -198,7 +199,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer]) .build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(firstServer, ServerDescription.builder().type(ServerType.UNKNOWN) @@ -229,7 +230,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(secondServer, REPLICA_SET_SECONDARY, allServers) factory.sendNotification(thirdServer, REPLICA_SET_PRIMARY, allServers) @@ -253,7 +254,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) when: def latch = new CountDownLatch(1) @@ -283,7 +284,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, allServers) when: @@ -305,7 +306,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) when: def secondServerLatch = selectServerAsync(cluster, secondServer, serverSelectionTimeoutMS) @@ -330,7 +331,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) when: def serverLatch = selectServerAsync(cluster, firstServer) @@ -350,7 +351,7 @@ class BaseClusterSpecification extends Specification { builder().mode(MULTIPLE) .hosts([firstServer, secondServer, thirdServer]) .build(), - factory) + factory, CLIENT_METADATA) when: selectServerAsyncAndGet(cluster, firstServer, serverSelectionTimeoutMS) diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/DefaultServerSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/DefaultServerSpecification.groovy index 21f03260818..ed18cb8ae86 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/DefaultServerSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/DefaultServerSpecification.groovy @@ -49,6 +49,7 @@ import spock.lang.Specification import java.util.concurrent.CountDownLatch +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT import static com.mongodb.MongoCredential.createCredential import static com.mongodb.connection.ClusterConnectionMode.MULTIPLE @@ -386,7 +387,7 @@ class DefaultServerSpecification extends Specification { } private Cluster mockCluster() { - new BaseCluster(new ClusterId(), ClusterSettings.builder().build(), Mock(ClusterableServerFactory)) { + new BaseCluster(new ClusterId(), ClusterSettings.builder().build(), Mock(ClusterableServerFactory), CLIENT_METADATA) { @Override protected void connect() { } diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/DnsMultiServerClusterSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/DnsMultiServerClusterSpecification.groovy index 2c381165acd..930e30b2c7b 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/DnsMultiServerClusterSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/DnsMultiServerClusterSpecification.groovy @@ -16,6 +16,7 @@ package com.mongodb.internal.connection +import com.mongodb.ClusterFixture import com.mongodb.MongoConfigurationException import com.mongodb.ServerAddress import com.mongodb.connection.ClusterId @@ -67,7 +68,7 @@ class DnsMultiServerClusterSpecification extends Specification { .srvHost(srvHost) .mode(MULTIPLE) .build(), - factory, dnsSrvRecordMonitorFactory) + factory, ClusterFixture.CLIENT_METADATA, dnsSrvRecordMonitorFactory) then: 'the monitor is created and started' initializer != null diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/MultiServerClusterSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/MultiServerClusterSpecification.groovy index e0f932f4963..48d44a7ed87 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/MultiServerClusterSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/MultiServerClusterSpecification.groovy @@ -28,6 +28,7 @@ import com.mongodb.internal.selector.WritableServerSelector import org.bson.types.ObjectId import spock.lang.Specification +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT import static com.mongodb.connection.ClusterConnectionMode.MULTIPLE import static com.mongodb.connection.ClusterType.REPLICA_SET @@ -66,7 +67,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE) .serverSelectionTimeout(1, MILLISECONDS) - .hosts([firstServer]).build(), factory) + .hosts([firstServer]).build(), factory, CLIENT_METADATA) sendNotification(firstServer, REPLICA_SET_PRIMARY) expect: @@ -77,7 +78,7 @@ class MultiServerClusterSpecification extends Specification { def 'should correct report description when connected to a primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, REPLICA_SET_PRIMARY) @@ -90,7 +91,7 @@ class MultiServerClusterSpecification extends Specification { def 'should not get servers snapshot when closed'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts(Arrays.asList(firstServer)).mode(MULTIPLE).build(), - factory) + factory, CLIENT_METADATA) cluster.close() when: @@ -105,7 +106,7 @@ class MultiServerClusterSpecification extends Specification { def 'should discover all hosts in the cluster when notified by the primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, secondServer, thirdServer]) @@ -117,7 +118,7 @@ class MultiServerClusterSpecification extends Specification { def 'should discover all hosts in the cluster when notified by a secondary and there is no primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, [firstServer, secondServer, thirdServer]) @@ -129,7 +130,7 @@ class MultiServerClusterSpecification extends Specification { def 'should discover all passives in the cluster'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer], [secondServer, thirdServer]) @@ -142,7 +143,7 @@ class MultiServerClusterSpecification extends Specification { given: def seedListAddress = new ServerAddress('127.0.0.1:27017') def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([seedListAddress]).mode(MULTIPLE).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(seedListAddress, REPLICA_SET_SECONDARY, [firstServer, secondServer], firstServer) @@ -155,7 +156,7 @@ class MultiServerClusterSpecification extends Specification { given: def seedListAddress = new ServerAddress('127.0.0.1:27017') def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().hosts([seedListAddress]).mode(MULTIPLE).build(), factory) + ClusterSettings.builder().hosts([seedListAddress]).mode(MULTIPLE).build(), factory, CLIENT_METADATA) when: factory.sendNotification(seedListAddress, REPLICA_SET_PRIMARY, [firstServer, secondServer], firstServer) @@ -167,7 +168,7 @@ class MultiServerClusterSpecification extends Specification { def 'should remove a server when it no longer appears in hosts reported by the primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory) + ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory, CLIENT_METADATA) sendNotification(firstServer, REPLICA_SET_PRIMARY) sendNotification(secondServer, REPLICA_SET_SECONDARY) sendNotification(thirdServer, REPLICA_SET_SECONDARY) @@ -184,7 +185,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster( CLUSTER_ID, ClusterSettings.builder().requiredClusterType(REPLICA_SET).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(secondServer, SHARD_ROUTER) @@ -198,7 +199,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster( CLUSTER_ID, ClusterSettings.builder().requiredClusterType(REPLICA_SET).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(secondServer, REPLICA_SET_GHOST, []) @@ -213,7 +214,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster( CLUSTER_ID, ClusterSettings.builder().requiredClusterType(REPLICA_SET).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(secondServer, REPLICA_SET_GHOST, [firstServer, secondServer], (String) null) // null replica set name @@ -228,7 +229,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster( CLUSTER_ID, ClusterSettings.builder().requiredClusterType(SHARDED).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) sendNotification(firstServer, SHARD_ROUTER) when: @@ -242,7 +243,7 @@ class MultiServerClusterSpecification extends Specification { def 'should remove a server of wrong type from discovered replica set'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer, secondServer]).build(), factory) + ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer, secondServer]).build(), factory, CLIENT_METADATA) sendNotification(firstServer, REPLICA_SET_PRIMARY) when: @@ -259,7 +260,7 @@ class MultiServerClusterSpecification extends Specification { ClusterSettings.builder() .serverSelectionTimeout(1, MILLISECONDS) .mode(MULTIPLE).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, STANDALONE) @@ -274,7 +275,7 @@ class MultiServerClusterSpecification extends Specification { ClusterSettings.builder() .serverSelectionTimeout(1, MILLISECONDS) .mode(MULTIPLE).hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, REPLICA_SET_GHOST) @@ -293,7 +294,7 @@ class MultiServerClusterSpecification extends Specification { def 'should invalidate existing primary when a new primary notifies'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) sendNotification(firstServer, REPLICA_SET_PRIMARY) when: @@ -307,7 +308,7 @@ class MultiServerClusterSpecification extends Specification { def 'should invalidate new primary if its electionId is less than the previously reported electionId'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, secondServer, thirdServer], new ObjectId(new Date(1000))) when: @@ -323,7 +324,7 @@ class MultiServerClusterSpecification extends Specification { given: def serverAddressAlias = new ServerAddress('alternate') def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().mode(MULTIPLE).hosts([serverAddressAlias]).build(), factory) + ClusterSettings.builder().mode(MULTIPLE).hosts([serverAddressAlias]).build(), factory, CLIENT_METADATA) when: sendNotification(serverAddressAlias, REPLICA_SET_PRIMARY) @@ -335,7 +336,7 @@ class MultiServerClusterSpecification extends Specification { def 'should retain a Standalone server given a hosts list of size 1'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, STANDALONE) @@ -348,7 +349,7 @@ class MultiServerClusterSpecification extends Specification { def 'should remove any Standalone server given a hosts list of size greater than one'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, STANDALONE) @@ -364,7 +365,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster( CLUSTER_ID, ClusterSettings.builder().hosts([secondServer]).mode(MULTIPLE).requiredReplicaSetName('test1').build(), - factory) + factory, CLIENT_METADATA) when: factory.sendNotification(secondServer, REPLICA_SET_PRIMARY, [firstServer, secondServer, thirdServer], 'test2') @@ -377,7 +378,7 @@ class MultiServerClusterSpecification extends Specification { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().serverSelectionTimeout(100, MILLISECONDS).hosts([firstServer]).mode(MULTIPLE).build(), - factory) + factory, CLIENT_METADATA) cluster.close() when: @@ -390,7 +391,7 @@ class MultiServerClusterSpecification extends Specification { def 'should ignore a notification from a server that has been removed'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, thirdServer]) when: @@ -403,7 +404,7 @@ class MultiServerClusterSpecification extends Specification { def 'should add servers from a secondary host list when there is no primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory) + ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, [firstServer, secondServer]) when: @@ -416,7 +417,7 @@ class MultiServerClusterSpecification extends Specification { def 'should add and removes servers from a primary host list when there is a primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory) + ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, secondServer]) when: @@ -435,7 +436,7 @@ class MultiServerClusterSpecification extends Specification { def 'should ignore a secondary host list when there is a primary'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, - ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory) + ClusterSettings.builder().hosts([firstServer, secondServer, thirdServer]).build(), factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, secondServer]) when: @@ -448,7 +449,7 @@ class MultiServerClusterSpecification extends Specification { def 'should ignore a notification from a server that is not ok'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) factory.sendNotification(firstServer, REPLICA_SET_PRIMARY, [firstServer, secondServer, thirdServer]) when: @@ -473,7 +474,7 @@ class MultiServerClusterSpecification extends Specification { when: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(MULTIPLE).hosts([firstServer]) - .addClusterListener(clusterListener).build(), factory) + .addClusterListener(clusterListener).build(), factory, CLIENT_METADATA) then: 1 * clusterListener.clusterOpening { it.clusterId == CLUSTER_ID } @@ -506,7 +507,7 @@ class MultiServerClusterSpecification extends Specification { def 'should connect to all servers'() { given: def cluster = new MultiServerCluster(CLUSTER_ID, ClusterSettings.builder().hosts([firstServer, secondServer]).build(), - factory) + factory, CLIENT_METADATA) when: cluster.connect() diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/SingleServerClusterSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/SingleServerClusterSpecification.groovy index 3ebd5c4eb0f..faa04a188f9 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/SingleServerClusterSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/SingleServerClusterSpecification.groovy @@ -28,6 +28,7 @@ import com.mongodb.event.ClusterListener import com.mongodb.internal.selector.WritableServerSelector import spock.lang.Specification +import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT import static com.mongodb.connection.ClusterConnectionMode.SINGLE import static com.mongodb.connection.ClusterType.REPLICA_SET @@ -54,7 +55,7 @@ class SingleServerClusterSpecification extends Specification { def 'should update description when the server connects'() { given: def cluster = new SingleServerCluster(CLUSTER_ID, - ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory) + ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory, CLIENT_METADATA) when: sendNotification(firstServer, STANDALONE) @@ -71,7 +72,7 @@ class SingleServerClusterSpecification extends Specification { def 'should get server when open'() { given: def cluster = new SingleServerCluster(CLUSTER_ID, - ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory) + ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory, CLIENT_METADATA) when: sendNotification(firstServer, STANDALONE) @@ -90,7 +91,7 @@ class SingleServerClusterSpecification extends Specification { def 'should not get servers snapshot when closed'() { given: def cluster = new SingleServerCluster(CLUSTER_ID, - ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory) + ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)).build(), factory, CLIENT_METADATA) cluster.close() when: @@ -108,7 +109,7 @@ class SingleServerClusterSpecification extends Specification { given: def cluster = new SingleServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(SINGLE).requiredClusterType(ClusterType.SHARDED).hosts(Arrays.asList(firstServer)).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, ServerType.REPLICA_SET_PRIMARY) @@ -125,7 +126,7 @@ class SingleServerClusterSpecification extends Specification { given: def cluster = new SingleServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(SINGLE).requiredReplicaSetName('test1').hosts(Arrays.asList(firstServer)).build(), - factory) + factory, CLIENT_METADATA) when: sendNotification(firstServer, ServerType.REPLICA_SET_PRIMARY, 'test1') @@ -141,7 +142,7 @@ class SingleServerClusterSpecification extends Specification { def 'getServer should throw when cluster is incompatible'() { given: def cluster = new SingleServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(SINGLE).hosts(Arrays.asList(firstServer)) - .serverSelectionTimeout(1, SECONDS).build(), factory) + .serverSelectionTimeout(1, SECONDS).build(), factory, CLIENT_METADATA) sendNotification(firstServer, getBuilder(firstServer).minWireVersion(1000).maxWireVersion(1000).build()) when: @@ -157,7 +158,7 @@ class SingleServerClusterSpecification extends Specification { def 'should connect to server'() { given: def cluster = new SingleServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(SINGLE).hosts([firstServer]).build(), - factory) + factory, CLIENT_METADATA) when: cluster.connect() @@ -181,7 +182,7 @@ class SingleServerClusterSpecification extends Specification { when: def cluster = new SingleServerCluster(CLUSTER_ID, ClusterSettings.builder().mode(SINGLE).hosts([firstServer]) .addClusterListener(listener).build(), - factory) + factory, CLIENT_METADATA) then: 1 * listener.clusterOpening { it.clusterId == CLUSTER_ID } diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt index 0aa0c582ff4..94058e0cab9 100644 --- a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt @@ -17,12 +17,8 @@ package com.mongodb.kotlin.client import com.mongodb.ClientSessionOptions import com.mongodb.MongoNamespace -import com.mongodb.client.MongoClient as JMongoClient import com.mongodb.client.model.bulk.ClientBulkWriteOptions import com.mongodb.client.model.bulk.ClientNamespacedWriteModel -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.full.declaredMemberProperties -import kotlin.test.assertEquals import org.bson.BsonDocument import org.bson.Document import org.junit.jupiter.api.Test @@ -35,6 +31,10 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals +import com.mongodb.client.MongoClient as JMongoClient class MongoClientTest { @@ -43,7 +43,14 @@ class MongoClientTest { @Test fun shouldHaveTheSameMethods() { - val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name }.toSet() + val jMongoClientFunctions = + JMongoClient::class + .declaredFunctions + .map { it.name } + // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } + .filterNot { it == "updateMetadata" } + .toSet() + val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() + MongoClient::class diff --git a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala index a888e33ae7f..e7f10b15417 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala @@ -16,10 +16,10 @@ package org.mongodb.scala -import com.mongodb.reactivestreams.client.{ MongoClient => JMongoClient } +import com.mongodb.reactivestreams.client.{MongoClient => JMongoClient} import org.bson.BsonDocument import org.mockito.Mockito.verify -import org.mongodb.scala.model.bulk.{ ClientBulkWriteOptions, ClientBulkWriteResult, ClientNamespacedWriteModel } +import org.mongodb.scala.model.bulk.{ClientBulkWriteOptions, ClientNamespacedWriteModel} import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ @@ -36,8 +36,11 @@ class MongoClientSpec extends BaseSpec with MockitoSugar { val local = classOf[MongoClient].getMethods.map(_.getName) wrapped.foreach((name: String) => { - val cleanedName = name.stripPrefix("get") - assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") + //TODO-JAVA-5871 remove this if statement, but leave the body. + if(!name.equals("updateMetadata")) { + val cleanedName = name.stripPrefix("get") + assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") + } }) } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 81c58e0f2ea..bb1ee2ccc9b 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -44,11 +44,11 @@ */ public abstract class AbstractClientMetadataProseTest { - protected TestCommandListener commandListener; - protected TestConnectionPoolListener connectionPoolListener; + private TestCommandListener commandListener; + private TestConnectionPoolListener connectionPoolListener; - protected abstract MongoClient createMongoClient(@Nullable final MongoDriverInformation driverInformation, - final MongoClientSettings mongoClientSettings); + protected abstract MongoClient createMongoClient(@Nullable MongoDriverInformation driverInformation, + MongoClientSettings mongoClientSettings); @BeforeEach public void setUp() { From 972fe9ded9ec722a7af3a2ef669ef81d9c553c33 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 7 May 2025 23:58:41 -0700 Subject: [PATCH 06/26] Fix tests. JAVA-5854 --- .../com/mongodb/client/AbstractClientMetadataProseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index bb1ee2ccc9b..cb95ffff14e 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -185,7 +185,7 @@ private Optional executePingAndCaptureMetadataHandshake(final Mong } protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { - return MongoClientSettings.builder() + return getMongoClientSettingsBuilder() .addCommandListener(commandListener) .applyToConnectionPoolSettings(builder -> builder.addConnectionPoolListener(connectionPoolListener)); From bcf9cc827ce0c4c5afd8262a37697e85152064fe Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 8 May 2025 00:01:09 -0700 Subject: [PATCH 07/26] Fix tests. JAVA-5854 --- .../com/mongodb/client/AbstractClientMetadataProseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index cb95ffff14e..c4fce942f92 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -185,7 +185,7 @@ private Optional executePingAndCaptureMetadataHandshake(final Mong } protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { - return getMongoClientSettingsBuilder() + return Fixture.getMongoClientSettingsBuilder() .addCommandListener(commandListener) .applyToConnectionPoolSettings(builder -> builder.addConnectionPoolListener(connectionPoolListener)); From 179b26231f73e3e19fec5917ec0bca7065a8fceb Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 8 May 2025 00:40:29 -0700 Subject: [PATCH 08/26] Spotless fix. JAVA-5854 --- .../client/coroutine/syncadapter/SyncMongoClient.kt | 2 +- .../mongodb/kotlin/client/syncadapter/SyncMongoClient.kt | 2 +- .../kotlin/com/mongodb/kotlin/client/MongoClientTest.kt | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt index 6d5f93a465f..7ce4d333bba 100644 --- a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt @@ -16,9 +16,9 @@ package com.mongodb.kotlin.client.coroutine.syncadapter import com.mongodb.MongoDriverInformation +import com.mongodb.client.MongoClient as JMongoClient import com.mongodb.connection.ClusterDescription import com.mongodb.kotlin.client.coroutine.MongoClient -import com.mongodb.client.MongoClient as JMongoClient internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoCluster(wrapped), JMongoClient { override fun close(): Unit = wrapped.close() diff --git a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt index 9493ba1221e..ddb6d63632f 100644 --- a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -16,9 +16,9 @@ package com.mongodb.kotlin.client.syncadapter import com.mongodb.MongoDriverInformation +import com.mongodb.client.MongoClient as JMongoClient import com.mongodb.connection.ClusterDescription import com.mongodb.kotlin.client.MongoClient -import com.mongodb.client.MongoClient as JMongoClient internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoCluster(wrapped), JMongoClient { override fun close(): Unit = wrapped.close() diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt index 94058e0cab9..7bd085abf7e 100644 --- a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt @@ -17,8 +17,12 @@ package com.mongodb.kotlin.client import com.mongodb.ClientSessionOptions import com.mongodb.MongoNamespace +import com.mongodb.client.MongoClient as JMongoClient import com.mongodb.client.model.bulk.ClientBulkWriteOptions import com.mongodb.client.model.bulk.ClientNamespacedWriteModel +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals import org.bson.BsonDocument import org.bson.Document import org.junit.jupiter.api.Test @@ -31,10 +35,6 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.full.declaredMemberProperties -import kotlin.test.assertEquals -import com.mongodb.client.MongoClient as JMongoClient class MongoClientTest { From bc43ba064eabf2585b2fad29039821c1e535020f Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 8 May 2025 23:28:16 -0700 Subject: [PATCH 09/26] Skip tests when auth is enabled. JAVA-5870 --- .../mongodb/reactivestreams/client/MongoClient.java | 13 ++++++++++++- .../src/main/com/mongodb/client/MongoClient.java | 12 ++++++++++++ .../client/AbstractClientMetadataProseTest.java | 6 ++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java index ffcaad19f35..ac493de92bf 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java @@ -60,6 +60,17 @@ public interface MongoClient extends MongoCluster, Closeable { */ ClusterDescription getClusterDescription(); - + /** + * Updates the {@link MongoClient} metadata by appending the provided {@link MongoDriverInformation} + * to the existing metadata. + *

+ * This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might be visible in + * the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the server. + *

+ * Note: Metadata is limited to 512 bytes; any excess will be truncated. + * + * @param mongoDriverInformation the driver information to append to the existing metadata + * @since 5.6 + */ void updateMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-sync/src/main/com/mongodb/client/MongoClient.java b/driver-sync/src/main/com/mongodb/client/MongoClient.java index 4b2a9114f52..76ff84e4124 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoClient.java +++ b/driver-sync/src/main/com/mongodb/client/MongoClient.java @@ -63,5 +63,17 @@ public interface MongoClient extends MongoCluster, Closeable { */ ClusterDescription getClusterDescription(); + /** + * Updates the {@link MongoClient} metadata by appending the provided {@link MongoDriverInformation} + * to the existing metadata. + *

+ * This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might be visible in + * the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the server. + *

+ * Note: Metadata is limited to 512 bytes; any excess will be truncated. + * + * @param mongoDriverInformation the driver information to append to the existing metadata + * @since 5.6 + */ void updateMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index c4fce942f92..db8900020ea 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -16,7 +16,6 @@ package com.mongodb.client; -import com.mongodb.ClusterFixture; import com.mongodb.MongoClientSettings; import com.mongodb.MongoDriverInformation; import com.mongodb.event.CommandStartedEvent; @@ -34,6 +33,8 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; +import static com.mongodb.ClusterFixture.isAuthenticated; +import static com.mongodb.ClusterFixture.isLoadBalanced; import static com.mongodb.ClusterFixture.sleep; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -52,7 +53,8 @@ protected abstract MongoClient createMongoClient(@Nullable MongoDriverInformatio @BeforeEach public void setUp() { - assumeFalse(ClusterFixture.isLoadBalanced()); + assumeFalse(isLoadBalanced()); + assumeFalse(isAuthenticated()); commandListener = new TestCommandListener(); connectionPoolListener = new TestConnectionPoolListener(); From 3f5fc91c61fd24e48a650e02c9d3dcc5e96a7dde Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 9 May 2025 11:44:47 -0700 Subject: [PATCH 10/26] Apply Scala spotless. JAVA-5870 --- .../org/mongodb/scala/syncadapter/SyncMongoClient.scala | 2 +- .../test/scala/org/mongodb/scala/MongoClientSpec.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala index 7002647196d..98cf9e45a61 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala @@ -1,7 +1,7 @@ package org.mongodb.scala.syncadapter import com.mongodb.MongoDriverInformation -import com.mongodb.client.{MongoClient => JMongoClient} +import com.mongodb.client.{ MongoClient => JMongoClient } import org.mongodb.scala.MongoClient case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrapped) with JMongoClient { diff --git a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala index e7f10b15417..e9044657d01 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala @@ -16,10 +16,10 @@ package org.mongodb.scala -import com.mongodb.reactivestreams.client.{MongoClient => JMongoClient} +import com.mongodb.reactivestreams.client.{ MongoClient => JMongoClient } import org.bson.BsonDocument import org.mockito.Mockito.verify -import org.mongodb.scala.model.bulk.{ClientBulkWriteOptions, ClientNamespacedWriteModel} +import org.mongodb.scala.model.bulk.{ ClientBulkWriteOptions, ClientNamespacedWriteModel } import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ @@ -36,8 +36,8 @@ class MongoClientSpec extends BaseSpec with MockitoSugar { val local = classOf[MongoClient].getMethods.map(_.getName) wrapped.foreach((name: String) => { - //TODO-JAVA-5871 remove this if statement, but leave the body. - if(!name.equals("updateMetadata")) { + // TODO-JAVA-5871 remove this if statement, but leave the body. + if (!name.equals("updateMetadata")) { val cleanedName = name.stripPrefix("get") assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") } From 61192d89223355c0796808dd82be974e6b8e3d2c Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 9 May 2025 17:12:10 -0700 Subject: [PATCH 11/26] Update tests. JAVA-5870 --- .../client/coroutine/MongoClientTest.kt | 11 ++- .../AbstractClientMetadataProseTest.java | 78 +++++++++++++------ 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt index fd66e4de31b..83aee5f0c71 100644 --- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt +++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt @@ -19,9 +19,6 @@ import com.mongodb.ClientSessionOptions import com.mongodb.MongoNamespace import com.mongodb.client.model.bulk.ClientBulkWriteOptions import com.mongodb.client.model.bulk.ClientNamespacedWriteModel -import com.mongodb.reactivestreams.client.MongoClient as JMongoClient -import kotlin.reflect.full.declaredFunctions -import kotlin.test.assertEquals import kotlinx.coroutines.runBlocking import org.bson.BsonDocument import org.bson.Document @@ -36,6 +33,9 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import reactor.core.publisher.Mono +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import com.mongodb.reactivestreams.client.MongoClient as JMongoClient class MongoClientTest { @@ -44,7 +44,10 @@ class MongoClientTest { @Test fun shouldHaveTheSameMethods() { - val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name }.toSet() + val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name } + // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } + .filterNot { it == "updateMetadata" } + .toSet() val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() assertEquals(jMongoClientFunctions, kMongoClientFunctions) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index db8900020ea..272d573382d 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -38,6 +38,7 @@ import static com.mongodb.ClusterFixture.sleep; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeFalse; /** @@ -92,12 +93,16 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { //then //TODO change get() to orElseThrow - clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - driverInformation = clientMetadata.getDocument("driver"); + BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); - assertThat(driverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); - assertThat(driverInformation.getString("version").getValue()).isEqualTo(generatedVersionName + "|1.0"); - assertThat(clientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName + "|Framework Platform"); + assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); + assertThat(updatedDriverInformation.getString("version").getValue()).isEqualTo(generatedVersionName + "|1.0"); + assertThat(updatedClientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName + "|Framework Platform"); + + assertThat(withRemovedKeys(updatedClientMetadata, "driver", "platform")) + .usingRecursiveAssertion() + .isEqualTo(withRemovedKeys(clientMetadata, "driver", "platform")); } } @@ -110,12 +115,12 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { .build())) { //TODO change get() to orElseThrow - BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - BsonDocument generatedDriverInformation = clientMetadata.getDocument("driver"); + BsonDocument generatedDriverInformation = initialClientMetadata.getDocument("driver"); String generatedDriverName = generatedDriverInformation.get("name").asString().getValue(); String generatedVersionName = generatedDriverInformation.get("version").asString().getValue(); - String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + String generatedPlatformName = initialClientMetadata.get("platform").asString().getValue(); //when sleep(5); // wait for connection to become idle @@ -127,12 +132,16 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { //then //TODO change get() to orElseThrow - clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - generatedDriverInformation = clientMetadata.getDocument("driver"); + BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument updatedDriverInformation = initialClientMetadata.getDocument("driver"); + + assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); + assertThat(updatedDriverInformation.getString("version").getValue()).endsWith(generatedVersionName + "|1.0"); + assertThat(updatedClientMetadata.getString("platform").getValue()).endsWith(generatedPlatformName + "|Framework Platform"); - assertThat(generatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); - assertThat(generatedDriverInformation.getString("version").getValue()).endsWith(generatedVersionName + "|1.0"); - assertThat(clientMetadata.getString("platform").getValue()).endsWith(generatedPlatformName + "|Framework Platform"); + assertThat(withRemovedKeys(updatedClientMetadata, "driver", "platform")) + .usingRecursiveAssertion() + .isEqualTo(withRemovedKeys(initialClientMetadata, "driver", "platform")); } } @@ -151,26 +160,42 @@ void shouldNotCloseExistingConnectionsToUpdateMetadata() { //TODO change get() to orElseThrow BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); BsonDocument driverInformation = clientMetadata.getDocument("driver"); - String generatedDriverName = driverInformation.get("name").asString().getValue(); - String generatedVersionName = driverInformation.get("version").asString().getValue(); - String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + assertNotNull(driverInformation); //when mongoClient.updateMetadata(initialWrappingLibraryDriverInformation); //then assertThat(executePingAndCaptureMetadataHandshake(mongoClient)).isEmpty(); - driverInformation = clientMetadata.getDocument("driver"); - - assertThat(driverInformation.getString("name").getValue()).isEqualTo(generatedDriverName); - assertThat(driverInformation.getString("version").getValue()).isEqualTo(generatedVersionName); - assertThat(clientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName); - assertFalse(connectionPoolListener.getEvents().stream().anyMatch(ConnectionClosedEvent.class::isInstance), "Expected no connection closed events"); } } + @Test + void shouldAppendAppendProvidedMetadatDuringInitialization() { + //given + MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() + .driverName("library") + .driverVersion("1.2") + .driverPlatform("Library Platform") + .build(); + + try (MongoClient mongoClient = createMongoClient(initialWrappingLibraryDriverInformation, getMongoClientSettingsBuilder() + .build())) { + + //when + //TODO change get() to orElseThrow + BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument driverInformation = clientMetadata.getDocument("driver"); + + //then + assertThat(driverInformation.get("name").asString().getValue()).endsWith("|library"); + assertThat(driverInformation.get("version").asString().getValue()).endsWith("|1.2"); + assertThat(clientMetadata.get("platform").asString().getValue()).endsWith("|Library Platform"); + } + } + private Optional executePingAndCaptureMetadataHandshake(final MongoClient mongoClient) { commandListener.reset(); mongoClient.getDatabase("admin") @@ -197,5 +222,14 @@ protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { public void tearDown() { InternalStreamConnection.setRecordEverything(false); } + + private static BsonDocument withRemovedKeys(final BsonDocument updatedClientMetadata, + final String... keysToFilter) { + BsonDocument clone = updatedClientMetadata.clone(); + for (String keyToRemove : keysToFilter) { + clone.remove(keyToRemove); + } + return clone; + } } From e9f8dd687fb36765747e22a4bd64df3df0731248 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 9 May 2025 17:51:45 -0700 Subject: [PATCH 12/26] Fix tests. JAVA-5870 --- .../client/AbstractClientMetadataProseTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 272d573382d..6680deed981 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -77,11 +77,11 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { .build())) { //TODO change get() to orElseThrow - BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - BsonDocument driverInformation = clientMetadata.getDocument("driver"); + BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument driverInformation = initialClientMetadata.getDocument("driver"); String generatedDriverName = driverInformation.get("name").asString().getValue(); String generatedVersionName = driverInformation.get("version").asString().getValue(); - String generatedPlatformName = clientMetadata.get("platform").asString().getValue(); + String generatedPlatformName = initialClientMetadata.get("platform").asString().getValue(); //when sleep(5); // wait for connection to become idle @@ -102,7 +102,7 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { assertThat(withRemovedKeys(updatedClientMetadata, "driver", "platform")) .usingRecursiveAssertion() - .isEqualTo(withRemovedKeys(clientMetadata, "driver", "platform")); + .isEqualTo(withRemovedKeys(initialClientMetadata, "driver", "platform")); } } @@ -133,7 +133,7 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { //then //TODO change get() to orElseThrow BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - BsonDocument updatedDriverInformation = initialClientMetadata.getDocument("driver"); + BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); assertThat(updatedDriverInformation.getString("version").getValue()).endsWith(generatedVersionName + "|1.0"); From 95c1bb193a0f19e55968590b04d24fec75139b00 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Sun, 18 May 2025 22:25:43 -0700 Subject: [PATCH 13/26] Add parametrized tests. JAVA-5870 --- .../connection/ClientMetadataHelper.java | 24 +++--- .../client/coroutine/MongoClientTest.kt | 11 ++- .../AbstractClientMetadataProseTest.java | 83 +++++++++++++------ 3 files changed, 78 insertions(+), 40 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index a7a0a8e8f57..8e3fd32a90e 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -32,6 +32,7 @@ import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -185,22 +186,25 @@ public static BsonDocument updateClientMedataDocument(final BsonDocument clientM BsonDocument updatedClientMetadataDocument = clientMetadataDocument.clone(); BsonDocument driverInformation = clientMetadataDocument.getDocument("driver"); - MongoDriverInformation.Builder builder = MongoDriverInformation.builder(mongoDriverInformation) - .driverName(driverInformation.getString("name").getValue()) - .driverVersion(driverInformation.getString("version").getValue()); + List updatedDriverNames = new ArrayList<>(); + List updatedDriverVersions = new ArrayList<>(); + List updateDriverPlatforms = new ArrayList<>(); - if (updatedClientMetadataDocument.containsKey("platform")) { - builder.driverPlatform(updatedClientMetadataDocument.getString("platform").getValue()); - } + updatedDriverNames.add(driverInformation.getString("name").getValue()); + updatedDriverNames.addAll(mongoDriverInformation.getDriverNames()); + + updatedDriverVersions.add(driverInformation.getString("version").getValue()); + updatedDriverVersions.addAll(mongoDriverInformation.getDriverVersions()); - MongoDriverInformation updatedDriverInformation = builder.build(); + updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); + updateDriverPlatforms.addAll(mongoDriverInformation.getDriverPlatforms()); tryWithLimit(updatedClientMetadataDocument, d -> { - putAtPath(d, "driver.name", listToString(updatedDriverInformation.getDriverNames())); - putAtPath(d, "driver.version", listToString(updatedDriverInformation.getDriverVersions())); + putAtPath(d, "driver.name", listToString(updatedDriverNames)); + putAtPath(d, "driver.version", listToString(updatedDriverVersions)); }); tryWithLimit(updatedClientMetadataDocument, d -> { - putAtPath(d, "platform", listToString(updatedDriverInformation.getDriverPlatforms())); + putAtPath(d, "platform", listToString(updateDriverPlatforms)); }); return updatedClientMetadataDocument; } diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt index 83aee5f0c71..13b5d5c22fb 100644 --- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt +++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt @@ -44,10 +44,13 @@ class MongoClientTest { @Test fun shouldHaveTheSameMethods() { - val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name } - // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } - .filterNot { it == "updateMetadata" } - .toSet() + val jMongoClientFunctions = + JMongoClient::class + .declaredFunctions + .map { it.name } + // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } + .filterNot { it == "updateMetadata" } + .toSet() val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() assertEquals(jMongoClientFunctions, kMongoClientFunctions) diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 6680deed981..9f3597f14d8 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -28,14 +28,19 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import static com.mongodb.ClusterFixture.isAuthenticated; import static com.mongodb.ClusterFixture.isLoadBalanced; import static com.mongodb.ClusterFixture.sleep; +import static java.util.Optional.ofNullable; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -62,8 +67,25 @@ public void setUp() { InternalStreamConnection.setRecordEverything(true); } - @Test - void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { + @AfterEach + public void tearDown() { + InternalStreamConnection.setRecordEverything(false); + } + + public static Stream provideDriverInformation() { + return Stream.of( + Arguments.of("1.0", "Framework", "Framework Platform"), + Arguments.of("1.0", "Framework", null), + Arguments.of(null, "Framework", "Framework Platform"), + Arguments.of(null, "Framework", null) + ); + } + + @ParameterizedTest + @MethodSource("provideDriverInformation") + void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization(@Nullable final String driverVersion, + @Nullable final String driverName, + @Nullable final String driverPlatform) { //given MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() .driverName("library") @@ -85,29 +107,31 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization() { //when sleep(5); // wait for connection to become idle - mongoClient.updateMetadata(MongoDriverInformation.builder() - .driverVersion("1.0") - .driverName("Framework") - .driverPlatform("Framework Platform") - .build()); + updateClientMetadata(driverVersion, driverName, driverPlatform, mongoClient); //then //TODO change get() to orElseThrow BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); - assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); - assertThat(updatedDriverInformation.getString("version").getValue()).isEqualTo(generatedVersionName + "|1.0"); - assertThat(updatedClientMetadata.getString("platform").getValue()).isEqualTo(generatedPlatformName + "|Framework Platform"); + String expectedDriverName = driverName == null ? generatedDriverName : generatedDriverName + "|" + driverName; + String expectedDriverVersion = driverVersion == null ? generatedVersionName : generatedVersionName + "|" + driverVersion; + String expectedDriverPlatform = driverPlatform == null ? generatedPlatformName : generatedPlatformName + "|" + driverPlatform; + assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(expectedDriverName); + assertThat(updatedDriverInformation.getString("version").getValue()).endsWith(expectedDriverVersion); + assertThat(updatedClientMetadata.getString("platform").getValue()).endsWith(expectedDriverPlatform); assertThat(withRemovedKeys(updatedClientMetadata, "driver", "platform")) .usingRecursiveAssertion() .isEqualTo(withRemovedKeys(initialClientMetadata, "driver", "platform")); } } - @Test - void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { + @ParameterizedTest + @MethodSource("provideDriverInformation") + void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable final String driverVersion, + @Nullable final String driverName, + @Nullable final String driverPlatform) { //given try (MongoClient mongoClient = createMongoClient(null, getMongoClientSettingsBuilder() .applyToConnectionPoolSettings(builder -> @@ -124,21 +148,20 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization() { //when sleep(5); // wait for connection to become idle - mongoClient.updateMetadata(MongoDriverInformation.builder() - .driverVersion("1.0") - .driverName("Framework") - .driverPlatform("Framework Platform") - .build()); + updateClientMetadata(driverVersion, driverName, driverPlatform, mongoClient); //then //TODO change get() to orElseThrow BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); - assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(generatedDriverName + "|Framework"); - assertThat(updatedDriverInformation.getString("version").getValue()).endsWith(generatedVersionName + "|1.0"); - assertThat(updatedClientMetadata.getString("platform").getValue()).endsWith(generatedPlatformName + "|Framework Platform"); + String expectedDriverName = driverName == null ? generatedDriverName : generatedDriverName + "|" + driverName; + String expectedDriverVersion = driverVersion == null ? generatedVersionName : generatedVersionName + "|" + driverVersion; + String expectedDriverPlatform = driverPlatform == null ? generatedPlatformName : generatedPlatformName + "|" + driverPlatform; + assertThat(updatedDriverInformation.getString("name").getValue()).isEqualTo(expectedDriverName); + assertThat(updatedDriverInformation.getString("version").getValue()).endsWith(expectedDriverVersion); + assertThat(updatedClientMetadata.getString("platform").getValue()).endsWith(expectedDriverPlatform); assertThat(withRemovedKeys(updatedClientMetadata, "driver", "platform")) .usingRecursiveAssertion() .isEqualTo(withRemovedKeys(initialClientMetadata, "driver", "platform")); @@ -172,8 +195,9 @@ void shouldNotCloseExistingConnectionsToUpdateMetadata() { } } + // Not a prose test. Additional test for better coverage. @Test - void shouldAppendAppendProvidedMetadatDuringInitialization() { + void shouldAppendProvidedMetadatDuringInitialization() { //given MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() .driverName("library") @@ -218,11 +242,6 @@ protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { builder.addConnectionPoolListener(connectionPoolListener)); } - @AfterEach - public void tearDown() { - InternalStreamConnection.setRecordEverything(false); - } - private static BsonDocument withRemovedKeys(final BsonDocument updatedClientMetadata, final String... keysToFilter) { BsonDocument clone = updatedClientMetadata.clone(); @@ -231,5 +250,17 @@ private static BsonDocument withRemovedKeys(final BsonDocument updatedClientMeta } return clone; } + + private static void updateClientMetadata(@Nullable final String driverVersion, + @Nullable final String driverName, + @Nullable final String driverPlatform, + final MongoClient mongoClient) { + MongoDriverInformation.Builder builder; + builder = MongoDriverInformation.builder(); + ofNullable(driverName).ifPresent(builder::driverName); + ofNullable(driverVersion).ifPresent(builder::driverVersion); + ofNullable(driverPlatform).ifPresent(builder::driverPlatform); + mongoClient.updateMetadata(builder.build()); + } } From dd63a49fcf075997691c641f3bfdb67de91be5f7 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Sun, 18 May 2025 22:34:29 -0700 Subject: [PATCH 14/26] Apply Kotlin spotless. JAVA-5870 --- .../com/mongodb/kotlin/client/coroutine/MongoClientTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt index 13b5d5c22fb..1053ba0d784 100644 --- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt +++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt @@ -19,6 +19,9 @@ import com.mongodb.ClientSessionOptions import com.mongodb.MongoNamespace import com.mongodb.client.model.bulk.ClientBulkWriteOptions import com.mongodb.client.model.bulk.ClientNamespacedWriteModel +import com.mongodb.reactivestreams.client.MongoClient as JMongoClient +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals import kotlinx.coroutines.runBlocking import org.bson.BsonDocument import org.bson.Document @@ -33,9 +36,6 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import reactor.core.publisher.Mono -import kotlin.reflect.full.declaredFunctions -import kotlin.test.assertEquals -import com.mongodb.reactivestreams.client.MongoClient as JMongoClient class MongoClientTest { From 89d67bb1bd13d6f81cb4621b9d32e3bef4bdae28 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 19 May 2025 16:12:55 -0700 Subject: [PATCH 15/26] Move update to separate variables. JAVA-5870 --- .../connection/ClientMetadataHelper.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index 8e3fd32a90e..4b1f74b3fc2 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -186,18 +186,22 @@ public static BsonDocument updateClientMedataDocument(final BsonDocument clientM BsonDocument updatedClientMetadataDocument = clientMetadataDocument.clone(); BsonDocument driverInformation = clientMetadataDocument.getDocument("driver"); - List updatedDriverNames = new ArrayList<>(); - List updatedDriverVersions = new ArrayList<>(); - List updateDriverPlatforms = new ArrayList<>(); + List driverNamesToAppend = mongoDriverInformation.getDriverNames(); + List driverVersionsToAppend = mongoDriverInformation.getDriverVersions(); + List driverPlatformsToAppend = mongoDriverInformation.getDriverPlatforms(); + + List updatedDriverNames = new ArrayList<>(driverNamesToAppend.size() + 1); + List updatedDriverVersions = new ArrayList<>(driverVersionsToAppend.size() + 1); + List updateDriverPlatforms = new ArrayList<>(driverPlatformsToAppend.size() + 1); updatedDriverNames.add(driverInformation.getString("name").getValue()); - updatedDriverNames.addAll(mongoDriverInformation.getDriverNames()); + updatedDriverNames.addAll(driverNamesToAppend); updatedDriverVersions.add(driverInformation.getString("version").getValue()); - updatedDriverVersions.addAll(mongoDriverInformation.getDriverVersions()); + updatedDriverVersions.addAll(driverVersionsToAppend); updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); - updateDriverPlatforms.addAll(mongoDriverInformation.getDriverPlatforms()); + updateDriverPlatforms.addAll(driverPlatformsToAppend); tryWithLimit(updatedClientMetadataDocument, d -> { putAtPath(d, "driver.name", listToString(updatedDriverNames)); From a8dc4fbdb2445c1d5c110f9b8ef50a7e70ea4b54 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 20 May 2025 20:04:05 -0700 Subject: [PATCH 16/26] Add lock for updates. JAVA-5870 --- .../com/mongodb/internal/connection/ClientMetadata.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java index 318da52ba6e..fa432545bac 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -20,6 +20,9 @@ import com.mongodb.lang.Nullable; import org.bson.BsonDocument; +import java.util.concurrent.locks.ReentrantLock; + +import static com.mongodb.internal.Locks.withLock; import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMedataDocument; @@ -29,6 +32,7 @@ *

This class is not part of the public API and may be removed or changed at any time

*/ public class ClientMetadata { + private final ReentrantLock updateLock = new ReentrantLock(); private volatile BsonDocument clientMetadataBsonDocument; public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { @@ -43,7 +47,9 @@ public BsonDocument getBsonDocument() { } public void append(final MongoDriverInformation mongoDriverInformation) { - this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument, mongoDriverInformation); + withLock(updateLock, () -> + this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument, mongoDriverInformation) + ); } } From 8ade58b3cd1dda08a0541636086d41b85d18d7a9 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 21 May 2025 12:18:09 -0700 Subject: [PATCH 17/26] Clone document before passing it as an argument. JAVA-5870 --- .../internal/connection/ClientMetadata.java | 2 +- .../internal/connection/ClientMetadataHelper.java | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java index fa432545bac..b1625c48915 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -48,7 +48,7 @@ public BsonDocument getBsonDocument() { public void append(final MongoDriverInformation mongoDriverInformation) { withLock(updateLock, () -> - this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument, mongoDriverInformation) + this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument.clone(), mongoDriverInformation) ); } } diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index 4b1f74b3fc2..1dcb3f68f97 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -180,10 +180,15 @@ static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { new BsonDocumentCodec().encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE; } - +/** + * Modifies the given client metadata document by appending the driver information. + * Driver name and version are appended atomically to the existing driver name and version if they do not exceed + * {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. + * + * Platform is appended separately to the existing platform if it does not exceed {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. + */ public static BsonDocument updateClientMedataDocument(final BsonDocument clientMetadataDocument, final MongoDriverInformation mongoDriverInformation) { - BsonDocument updatedClientMetadataDocument = clientMetadataDocument.clone(); BsonDocument driverInformation = clientMetadataDocument.getDocument("driver"); List driverNamesToAppend = mongoDriverInformation.getDriverNames(); @@ -203,14 +208,14 @@ public static BsonDocument updateClientMedataDocument(final BsonDocument clientM updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); updateDriverPlatforms.addAll(driverPlatformsToAppend); - tryWithLimit(updatedClientMetadataDocument, d -> { + tryWithLimit(clientMetadataDocument, d -> { putAtPath(d, "driver.name", listToString(updatedDriverNames)); putAtPath(d, "driver.version", listToString(updatedDriverVersions)); }); - tryWithLimit(updatedClientMetadataDocument, d -> { + tryWithLimit(clientMetadataDocument, d -> { putAtPath(d, "platform", listToString(updateDriverPlatforms)); }); - return updatedClientMetadataDocument; + return clientMetadataDocument; } public enum ContainerRuntime { From 71350fad71704bfcb6af0d60eea831ce88fe6e92 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 21 May 2025 13:09:54 -0700 Subject: [PATCH 18/26] Rename to "appendMetadata". JAVA-5870 --- .../kotlin/client/coroutine/syncadapter/SyncMongoClient.kt | 2 +- .../com/mongodb/kotlin/client/coroutine/MongoClientTest.kt | 4 ++-- .../mongodb/kotlin/client/syncadapter/SyncMongoClient.kt | 2 +- .../kotlin/com/mongodb/kotlin/client/MongoClientTest.kt | 4 ++-- .../com/mongodb/reactivestreams/client/MongoClient.java | 6 +++--- .../reactivestreams/client/internal/MongoClientImpl.java | 2 +- .../reactivestreams/client/syncadapter/SyncMongoClient.java | 4 ++-- .../org/mongodb/scala/syncadapter/SyncMongoClient.scala | 4 ++-- .../src/test/scala/org/mongodb/scala/MongoClientSpec.scala | 6 +++--- driver-sync/src/main/com/mongodb/client/MongoClient.java | 5 ++--- .../main/com/mongodb/client/internal/MongoClientImpl.java | 2 +- .../com/mongodb/client/AbstractClientMetadataProseTest.java | 6 +++--- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt index 7ce4d333bba..ff28b4fa022 100644 --- a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt @@ -25,7 +25,7 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun getClusterDescription(): ClusterDescription = wrapped.getClusterDescription() - override fun updateMetadata(mongoDriverInformation: MongoDriverInformation) { + override fun appendMetadata(mongoDriverInformation: MongoDriverInformation) { throw UnsupportedOperationException("TODO-JAVA-5871") } } diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt index 1053ba0d784..65c8d33cfdd 100644 --- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt +++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt @@ -48,8 +48,8 @@ class MongoClientTest { JMongoClient::class .declaredFunctions .map { it.name } - // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } - .filterNot { it == "updateMetadata" } + // TODO-JAVA-5871 remove .filterNot { it == "appendMetadata" } + .filterNot { it == "appendMetadata" } .toSet() val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() diff --git a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt index ddb6d63632f..b973e75de40 100644 --- a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -24,7 +24,7 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun close(): Unit = wrapped.close() override fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription - override fun updateMetadata(mongoDriverInformation: MongoDriverInformation) { + override fun appendMetadata(mongoDriverInformation: MongoDriverInformation) { throw UnsupportedOperationException("TODO-JAVA-5871") } } diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt index 7bd085abf7e..3254f951ed7 100644 --- a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt @@ -47,8 +47,8 @@ class MongoClientTest { JMongoClient::class .declaredFunctions .map { it.name } - // TODO-JAVA-5871 remove .filterNot { it == "updateMetadata" } - .filterNot { it == "updateMetadata" } + // TODO-JAVA-5871 remove .filterNot { it == "appendMetadata" } + .filterNot { it == "appendMetadata" } .toSet() val kMongoClientFunctions = diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java index ac493de92bf..87a3148b8b2 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/MongoClient.java @@ -61,8 +61,8 @@ public interface MongoClient extends MongoCluster, Closeable { ClusterDescription getClusterDescription(); /** - * Updates the {@link MongoClient} metadata by appending the provided {@link MongoDriverInformation} - * to the existing metadata. + * Appends the provided {@link MongoDriverInformation} to the existing metadata. + * *

* This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might be visible in * the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the server. @@ -72,5 +72,5 @@ public interface MongoClient extends MongoCluster, Closeable { * @param mongoDriverInformation the driver information to append to the existing metadata * @since 5.6 */ - void updateMetadata(MongoDriverInformation mongoDriverInformation); + void appendMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java index 96cc6d819d6..473ec0915d0 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/MongoClientImpl.java @@ -327,7 +327,7 @@ public ClusterDescription getClusterDescription() { } @Override - public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { + public void appendMetadata(final MongoDriverInformation mongoDriverInformation) { getCluster().getClientMetadata().append(mongoDriverInformation); } } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java index 73a4bde4545..3c67440c675 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/syncadapter/SyncMongoClient.java @@ -313,7 +313,7 @@ public ClusterDescription getClusterDescription() { } @Override - public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { - wrapped.updateMetadata(mongoDriverInformation); + public void appendMetadata(final MongoDriverInformation mongoDriverInformation) { + wrapped.appendMetadata(mongoDriverInformation); } } diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala index 98cf9e45a61..3778329b143 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala @@ -1,7 +1,7 @@ package org.mongodb.scala.syncadapter import com.mongodb.MongoDriverInformation -import com.mongodb.client.{ MongoClient => JMongoClient } +import com.mongodb.client.{MongoClient => JMongoClient} import org.mongodb.scala.MongoClient case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrapped) with JMongoClient { @@ -10,6 +10,6 @@ case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrappe override def getClusterDescription = throw new UnsupportedOperationException - override def updateMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + override def appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = throw new UnsupportedOperationException("TODO-JAVA-5871") } diff --git a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala index e9044657d01..01e0abd37b2 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala @@ -16,10 +16,10 @@ package org.mongodb.scala -import com.mongodb.reactivestreams.client.{ MongoClient => JMongoClient } +import com.mongodb.reactivestreams.client.{MongoClient => JMongoClient} import org.bson.BsonDocument import org.mockito.Mockito.verify -import org.mongodb.scala.model.bulk.{ ClientBulkWriteOptions, ClientNamespacedWriteModel } +import org.mongodb.scala.model.bulk.{ClientBulkWriteOptions, ClientNamespacedWriteModel} import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ @@ -37,7 +37,7 @@ class MongoClientSpec extends BaseSpec with MockitoSugar { wrapped.foreach((name: String) => { // TODO-JAVA-5871 remove this if statement, but leave the body. - if (!name.equals("updateMetadata")) { + if (!name.equals("appendMetadata")) { val cleanedName = name.stripPrefix("get") assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") } diff --git a/driver-sync/src/main/com/mongodb/client/MongoClient.java b/driver-sync/src/main/com/mongodb/client/MongoClient.java index 76ff84e4124..e61ebf92566 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoClient.java +++ b/driver-sync/src/main/com/mongodb/client/MongoClient.java @@ -64,8 +64,7 @@ public interface MongoClient extends MongoCluster, Closeable { ClusterDescription getClusterDescription(); /** - * Updates the {@link MongoClient} metadata by appending the provided {@link MongoDriverInformation} - * to the existing metadata. + * Appends the provided {@link MongoDriverInformation} to the existing metadata. *

* This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might be visible in * the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the server. @@ -75,5 +74,5 @@ public interface MongoClient extends MongoCluster, Closeable { * @param mongoDriverInformation the driver information to append to the existing metadata * @since 5.6 */ - void updateMetadata(MongoDriverInformation mongoDriverInformation); + void appendMetadata(MongoDriverInformation mongoDriverInformation); } diff --git a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java index c1c5288bf9e..47e3266cbf1 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -136,7 +136,7 @@ public ClusterDescription getClusterDescription() { } @Override - public void updateMetadata(final MongoDriverInformation mongoDriverInformation) { + public void appendMetadata(final MongoDriverInformation mongoDriverInformation) { delegate.getCluster().getClientMetadata().append(mongoDriverInformation); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 9f3597f14d8..e798e0ea87d 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -169,7 +169,7 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable } @Test - void shouldNotCloseExistingConnectionsToUpdateMetadata() { + void shouldNotCloseExistingConnectionsToAppendMetadata() { //given MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() .driverName("library") @@ -186,7 +186,7 @@ void shouldNotCloseExistingConnectionsToUpdateMetadata() { assertNotNull(driverInformation); //when - mongoClient.updateMetadata(initialWrappingLibraryDriverInformation); + mongoClient.appendMetadata(initialWrappingLibraryDriverInformation); //then assertThat(executePingAndCaptureMetadataHandshake(mongoClient)).isEmpty(); @@ -260,7 +260,7 @@ private static void updateClientMetadata(@Nullable final String driverVersion, ofNullable(driverName).ifPresent(builder::driverName); ofNullable(driverVersion).ifPresent(builder::driverVersion); ofNullable(driverPlatform).ifPresent(builder::driverPlatform); - mongoClient.updateMetadata(builder.build()); + mongoClient.appendMetadata(builder.build()); } } From 9890aa18a98afb0ef15aeb1cebe4f8e16c888cea Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 26 May 2025 23:57:54 -0700 Subject: [PATCH 19/26] Add ReadWriteLock and rename method. JAVA-5870 --- .../internal/connection/ClientMetadata.java | 16 +++++----- .../connection/ClientMetadataHelper.java | 31 ++++++++++--------- .../ClientMetadataHelperProseTest.java | 4 +-- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java index b1625c48915..b3a9abb3933 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -20,11 +20,11 @@ import com.mongodb.lang.Nullable; import org.bson.BsonDocument; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.mongodb.internal.Locks.withLock; import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; -import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMedataDocument; +import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMetadataDocument; /** * Represents metadata of the current MongoClient. @@ -32,11 +32,13 @@ *

This class is not part of the public API and may be removed or changed at any time

*/ public class ClientMetadata { - private final ReentrantLock updateLock = new ReentrantLock(); - private volatile BsonDocument clientMetadataBsonDocument; + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private BsonDocument clientMetadataBsonDocument; public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { - this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); + withLock(readWriteLock.writeLock(), () -> { + this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); + }); } /** @@ -47,8 +49,8 @@ public BsonDocument getBsonDocument() { } public void append(final MongoDriverInformation mongoDriverInformation) { - withLock(updateLock, () -> - this.clientMetadataBsonDocument = updateClientMedataDocument(clientMetadataBsonDocument.clone(), mongoDriverInformation) + withLock(readWriteLock.writeLock(), () -> + this.clientMetadataBsonDocument = updateClientMetadataDocument(clientMetadataBsonDocument.clone(), mongoDriverInformation) ); } } diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java index 1dcb3f68f97..2675e0f8efd 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java @@ -180,29 +180,30 @@ static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { new BsonDocumentCodec().encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE; } -/** - * Modifies the given client metadata document by appending the driver information. - * Driver name and version are appended atomically to the existing driver name and version if they do not exceed - * {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. - * - * Platform is appended separately to the existing platform if it does not exceed {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. - */ - public static BsonDocument updateClientMedataDocument(final BsonDocument clientMetadataDocument, - final MongoDriverInformation mongoDriverInformation) { - BsonDocument driverInformation = clientMetadataDocument.getDocument("driver"); - List driverNamesToAppend = mongoDriverInformation.getDriverNames(); - List driverVersionsToAppend = mongoDriverInformation.getDriverVersions(); - List driverPlatformsToAppend = mongoDriverInformation.getDriverPlatforms(); + /** + * Modifies the given client metadata document by appending the driver information. + * Driver name and version are appended atomically to the existing driver name and version if they do not exceed + * {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. + *

+ * Platform is appended separately to the existing platform if it does not exceed {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. + */ + public static BsonDocument updateClientMetadataDocument(final BsonDocument clientMetadataDocument, + final MongoDriverInformation driverInformationToAppend) { + BsonDocument currentDriverInformation = clientMetadataDocument.getDocument("driver"); + + List driverNamesToAppend = driverInformationToAppend.getDriverNames(); + List driverVersionsToAppend = driverInformationToAppend.getDriverVersions(); + List driverPlatformsToAppend = driverInformationToAppend.getDriverPlatforms(); List updatedDriverNames = new ArrayList<>(driverNamesToAppend.size() + 1); List updatedDriverVersions = new ArrayList<>(driverVersionsToAppend.size() + 1); List updateDriverPlatforms = new ArrayList<>(driverPlatformsToAppend.size() + 1); - updatedDriverNames.add(driverInformation.getString("name").getValue()); + updatedDriverNames.add(currentDriverInformation.getString("name").getValue()); updatedDriverNames.addAll(driverNamesToAppend); - updatedDriverVersions.add(driverInformation.getString("version").getValue()); + updatedDriverVersions.add(currentDriverInformation.getString("version").getValue()); updatedDriverVersions.addAll(driverVersionsToAppend); updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java index 3aedaee2848..1cbb00ee8c6 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java @@ -43,7 +43,7 @@ import static com.mongodb.client.WithWrapper.withWrapper; import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.connection.ClientMetadataHelper.getOperatingSystemType; -import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMedataDocument; +import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMetadataDocument; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -349,7 +349,7 @@ void testUpdateClientMetadataDocument() { .build(); //when - BsonDocument updatedClientMetadata = updateClientMedataDocument(initialClientMetadataDocument, metadataToAppend); + BsonDocument updatedClientMetadata = updateClientMetadataDocument(initialClientMetadataDocument, metadataToAppend); //then assertEquals( From 28f7d88521638389ed5c75258a3ff905513e3c1c Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 3 Jun 2025 23:56:50 -0700 Subject: [PATCH 20/26] Add parametrized tests. --- .../ClientMetadataHelperProseTest.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java index 1cbb00ee8c6..ae7bc9e5e7c 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java @@ -28,7 +28,9 @@ import org.bson.codecs.DocumentCodec; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -38,12 +40,14 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import static com.mongodb.client.CrudTestHelper.repeat; import static com.mongodb.client.WithWrapper.withWrapper; import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.connection.ClientMetadataHelper.getOperatingSystemType; import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMetadataDocument; +import static java.util.Optional.ofNullable; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -320,9 +324,22 @@ public void testCreateClientMetadataDocument(@Nullable final String appName, fin createClientMetadataDocument(appName, driverInformation)); } + public static java.util.stream.Stream provideDriverInformation() { + return Stream.of( + Arguments.of("1.0", "Framework", "Framework Platform"), + Arguments.of("1.0", "Framework", null), + Arguments.of(null, "Framework", "Framework Platform"), + Arguments.of(null, null, "Framework Platform"), + Arguments.of(null, "Framework", null) + ); + } - @Test - void testUpdateClientMetadataDocument() { + + @ParameterizedTest + @MethodSource("provideDriverInformation") + void testUpdateClientMetadataDocument(@Nullable final String driverVersion, + @Nullable final String driverName, + @Nullable final String driverPlatform) { //given MongoDriverInformation initialDriverInformation = MongoDriverInformation.builder() .driverName("mongo-spark") @@ -335,11 +352,12 @@ void testUpdateClientMetadataDocument() { createExpectedClientMetadataDocument(null, initialDriverInformation), initialClientMetadataDocument); - MongoDriverInformation metadataToAppend = MongoDriverInformation.builder() - .driverVersion("2.0.0") - .driverName("Framework") - .driverPlatform("JDK 99") - .build(); + MongoDriverInformation.Builder builder; + builder = MongoDriverInformation.builder(); + ofNullable(driverName).ifPresent(builder::driverName); + ofNullable(driverVersion).ifPresent(builder::driverVersion); + ofNullable(driverPlatform).ifPresent(builder::driverPlatform); + MongoDriverInformation metadataToAppend = builder.build(); //We pass metadataToAppend to a builder and prepend with initial driver information. MongoDriverInformation expectedUpdatedMetadata = MongoDriverInformation.builder(metadataToAppend) @@ -349,7 +367,7 @@ void testUpdateClientMetadataDocument() { .build(); //when - BsonDocument updatedClientMetadata = updateClientMetadataDocument(initialClientMetadataDocument, metadataToAppend); + BsonDocument updatedClientMetadata = updateClientMetadataDocument(initialClientMetadataDocument.clone(), metadataToAppend); //then assertEquals( From 246a040e676807dc9868eaff40cbd90761abee24 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 3 Jun 2025 23:59:24 -0700 Subject: [PATCH 21/26] Add readLock. --- .../main/com/mongodb/internal/connection/ClientMetadata.java | 2 +- .../scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala | 2 +- .../src/test/scala/org/mongodb/scala/MongoClientSpec.scala | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java index b3a9abb3933..e04abf195c4 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -36,7 +36,7 @@ public class ClientMetadata { private BsonDocument clientMetadataBsonDocument; public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { - withLock(readWriteLock.writeLock(), () -> { + withLock(readWriteLock.readLock(), () -> { this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); }); } diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala index 3778329b143..d5e4bba01ca 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala @@ -1,7 +1,7 @@ package org.mongodb.scala.syncadapter import com.mongodb.MongoDriverInformation -import com.mongodb.client.{MongoClient => JMongoClient} +import com.mongodb.client.{ MongoClient => JMongoClient } import org.mongodb.scala.MongoClient case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrapped) with JMongoClient { diff --git a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala index 01e0abd37b2..59ae02edaa2 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala @@ -16,10 +16,10 @@ package org.mongodb.scala -import com.mongodb.reactivestreams.client.{MongoClient => JMongoClient} +import com.mongodb.reactivestreams.client.{ MongoClient => JMongoClient } import org.bson.BsonDocument import org.mockito.Mockito.verify -import org.mongodb.scala.model.bulk.{ClientBulkWriteOptions, ClientNamespacedWriteModel} +import org.mongodb.scala.model.bulk.{ ClientBulkWriteOptions, ClientNamespacedWriteModel } import org.scalatestplus.mockito.MockitoSugar import scala.collection.JavaConverters._ From 8a582946b81c8bee9f55e394dd63bda86d444d38 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 4 Jun 2025 09:49:26 -0700 Subject: [PATCH 22/26] Fix readLock issue. --- .../main/com/mongodb/internal/connection/ClientMetadata.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java index e04abf195c4..e48327cd5a3 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -36,7 +36,7 @@ public class ClientMetadata { private BsonDocument clientMetadataBsonDocument; public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { - withLock(readWriteLock.readLock(), () -> { + withLock(readWriteLock.writeLock(), () -> { this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); }); } @@ -45,7 +45,7 @@ public ClientMetadata(@Nullable final String applicationName, final MongoDriverI * Returns mutable BsonDocument that represents the client metadata. */ public BsonDocument getBsonDocument() { - return clientMetadataBsonDocument; + return withLock(readWriteLock.readLock(), () -> clientMetadataBsonDocument); } public void append(final MongoDriverInformation mongoDriverInformation) { From 5c7a6e3d261d188c36f0d27c18db1ff95dcf2e6b Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 6 Jun 2025 15:34:15 -0700 Subject: [PATCH 23/26] Add Kotlin and Scala API. --- .../coroutine/syncadapter/SyncMongoClient.kt | 5 ++-- .../kotlin/client/coroutine/MongoClient.kt | 15 +++++++++++ .../client/coroutine/MongoClientTest.kt | 25 +++++++++++++------ .../client/syncadapter/SyncMongoClient.kt | 5 ++-- .../com/mongodb/kotlin/client/MongoClient.kt | 15 +++++++++++ .../mongodb/kotlin/client/MongoClientTest.kt | 25 +++++++++++++------ .../scala/syncadapter/SyncMongoClient.scala | 2 +- .../scala/org/mongodb/scala/MongoClient.scala | 16 ++++++++++++ .../org/mongodb/scala/MongoClientSpec.scala | 13 ++++++---- 9 files changed, 95 insertions(+), 26 deletions(-) diff --git a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt index ff28b4fa022..4a97557d14a 100644 --- a/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-coroutine/src/integrationTest/kotlin/com/mongodb/kotlin/client/coroutine/syncadapter/SyncMongoClient.kt @@ -25,7 +25,6 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun getClusterDescription(): ClusterDescription = wrapped.getClusterDescription() - override fun appendMetadata(mongoDriverInformation: MongoDriverInformation) { - throw UnsupportedOperationException("TODO-JAVA-5871") - } + override fun appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + wrapped.appendMetadata(mongoDriverInformation) } diff --git a/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/MongoClient.kt b/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/MongoClient.kt index 68b937588d9..64832903b40 100644 --- a/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/MongoClient.kt +++ b/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/MongoClient.kt @@ -110,6 +110,21 @@ public class MongoClient(private val wrapped: JMongoClient) : MongoCluster(wrapp * @see com.mongodb.MongoClientSettings.Builder.applyToClusterSettings */ public fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription + + /** + * Appends the provided [MongoDriverInformation] to the existing metadata. + * + * This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might + * be visible in the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the + * server. + * + * **Note:** Metadata is limited to 512 bytes; any excess will be truncated. + * + * @param mongoDriverInformation the driver information to append to the existing metadata + * @since 5.6 + */ + public fun appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + wrapped.appendMetadata(mongoDriverInformation) } /** diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt index 65c8d33cfdd..b1dc72e6a81 100644 --- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt +++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/MongoClientTest.kt @@ -16,6 +16,7 @@ package com.mongodb.kotlin.client.coroutine import com.mongodb.ClientSessionOptions +import com.mongodb.MongoDriverInformation import com.mongodb.MongoNamespace import com.mongodb.client.model.bulk.ClientBulkWriteOptions import com.mongodb.client.model.bulk.ClientNamespacedWriteModel @@ -44,13 +45,7 @@ class MongoClientTest { @Test fun shouldHaveTheSameMethods() { - val jMongoClientFunctions = - JMongoClient::class - .declaredFunctions - .map { it.name } - // TODO-JAVA-5871 remove .filterNot { it == "appendMetadata" } - .filterNot { it == "appendMetadata" } - .toSet() + val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name }.toSet() val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() assertEquals(jMongoClientFunctions, kMongoClientFunctions) @@ -76,6 +71,22 @@ class MongoClientTest { verifyNoMoreInteractions(wrapped) } + @Test + fun shouldCallTheUnderlyingAppendMetadata() { + val mongoClient = MongoClient(wrapped) + + val mongoDriverInformation = + MongoDriverInformation.builder() + .driverName("kotlin") + .driverPlatform("kotlin/${KotlinVersion.CURRENT}") + .build() + + mongoClient.appendMetadata(mongoDriverInformation) + + verify(wrapped).appendMetadata(mongoDriverInformation) + verifyNoMoreInteractions(wrapped) + } + @Test fun shouldCallTheUnderlyingGetDatabase() { val mongoClient = MongoClient(wrapped) diff --git a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt index b973e75de40..02c58833df5 100644 --- a/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt +++ b/driver-kotlin-sync/src/integrationTest/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -24,7 +24,6 @@ internal class SyncMongoClient(override val wrapped: MongoClient) : SyncMongoClu override fun close(): Unit = wrapped.close() override fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription - override fun appendMetadata(mongoDriverInformation: MongoDriverInformation) { - throw UnsupportedOperationException("TODO-JAVA-5871") - } + override fun appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + wrapped.appendMetadata(mongoDriverInformation) } diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt index 4d8d2f26cc0..c71e59520b6 100644 --- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt @@ -109,6 +109,21 @@ public class MongoClient(private val wrapped: JMongoClient) : MongoCluster(wrapp */ public val clusterDescription: ClusterDescription get() = wrapped.clusterDescription + + /** + * Appends the provided [MongoDriverInformation] to the existing metadata. + * + * This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might + * be visible in the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the + * server. + * + * **Note:** Metadata is limited to 512 bytes; any excess will be truncated. + * + * @param mongoDriverInformation the driver information to append to the existing metadata + * @since 5.6 + */ + public fun appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + wrapped.appendMetadata(mongoDriverInformation) } /** diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt index 3254f951ed7..a6f67b22ce7 100644 --- a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt @@ -16,6 +16,7 @@ package com.mongodb.kotlin.client import com.mongodb.ClientSessionOptions +import com.mongodb.MongoDriverInformation import com.mongodb.MongoNamespace import com.mongodb.client.MongoClient as JMongoClient import com.mongodb.client.model.bulk.ClientBulkWriteOptions @@ -43,13 +44,7 @@ class MongoClientTest { @Test fun shouldHaveTheSameMethods() { - val jMongoClientFunctions = - JMongoClient::class - .declaredFunctions - .map { it.name } - // TODO-JAVA-5871 remove .filterNot { it == "appendMetadata" } - .filterNot { it == "appendMetadata" } - .toSet() + val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name }.toSet() val kMongoClientFunctions = MongoClient::class.declaredFunctions.map { it.name }.toSet() + @@ -81,6 +76,22 @@ class MongoClientTest { verifyNoMoreInteractions(wrapped) } + @Test + fun shouldCallTheUnderlyingAppendMetadata() { + val mongoClient = MongoClient(wrapped) + + val mongoDriverInformation = + MongoDriverInformation.builder() + .driverName("kotlin") + .driverPlatform("kotlin/${KotlinVersion.CURRENT}") + .build() + + mongoClient.appendMetadata(mongoDriverInformation) + + verify(wrapped).appendMetadata(mongoDriverInformation) + verifyNoMoreInteractions(wrapped) + } + @Test fun shouldCallTheUnderlyingGetDatabase() { val mongoClient = MongoClient(wrapped) diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala index d5e4bba01ca..b0617e95fd7 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoClient.scala @@ -11,5 +11,5 @@ case class SyncMongoClient(wrapped: MongoClient) extends SyncMongoCluster(wrappe override def getClusterDescription = throw new UnsupportedOperationException override def appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = - throw new UnsupportedOperationException("TODO-JAVA-5871") + wrapped.appendMetadata(mongoDriverInformation) } diff --git a/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala b/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala index c6849c550c1..ba4510d308d 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala @@ -132,4 +132,20 @@ case class MongoClient(private val wrapped: JMongoClient) extends MongoCluster(w */ def getClusterDescription: ClusterDescription = wrapped.getClusterDescription + + /** + * Appends the provided [[MongoDriverInformation]] to the existing metadata. + * + * + * This enables frameworks and libraries to include identifying metadata (e.g., name, version, platform) which might be visible in + * the MongoD/MongoS logs. This can assist with diagnostics by making client identity visible to the server. + * + * + * **Note:** Metadata is limited to 512 bytes; any excess will be truncated. + * + * @param mongoDriverInformation the driver information to append to the existing metadata + * @since 5.6 + */ + def appendMetadata(mongoDriverInformation: MongoDriverInformation): Unit = + wrapped.appendMetadata(mongoDriverInformation) } diff --git a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala index 59ae02edaa2..ca5b4f8734e 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/MongoClientSpec.scala @@ -36,11 +36,8 @@ class MongoClientSpec extends BaseSpec with MockitoSugar { val local = classOf[MongoClient].getMethods.map(_.getName) wrapped.foreach((name: String) => { - // TODO-JAVA-5871 remove this if statement, but leave the body. - if (!name.equals("appendMetadata")) { - val cleanedName = name.stripPrefix("get") - assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") - } + val cleanedName = name.stripPrefix("get") + assert(local.contains(name) | local.contains(cleanedName.head.toLower + cleanedName.tail), s"Missing: $name") }) } @@ -139,4 +136,10 @@ class MongoClientSpec extends BaseSpec with MockitoSugar { mongoClient.getClusterDescription verify(wrapped).getClusterDescription } + + it should "call the underlying appendMetadata" in { + val driverInformation = MongoDriverInformation.builder().build() + mongoClient.appendMetadata(driverInformation) + verify(wrapped).appendMetadata(driverInformation) + } } From f466d08002dd1ee4b54665d588607ab398122b40 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 9 Jun 2025 21:43:18 -0700 Subject: [PATCH 24/26] Add unified tests. --- .gitmodules | 3 +- .../client/unified/ClientMetadataTest.java | 28 ++++++++++++++++++ .../AbstractClientMetadataProseTest.java | 27 ----------------- .../mongodb/client/ClientMetadataTest.java | 29 +++++++++++++++++++ .../client/unified/UnifiedCrudHelper.java | 20 +++++++++++++ .../mongodb/client/unified/UnifiedTest.java | 2 ++ 6 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/ClientMetadataTest.java diff --git a/.gitmodules b/.gitmodules index a9ac62f04bb..58e84ab0966 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "specifications"] path = driver-core/src/test/resources/specifications - url = https://github.com/mongodb/specifications + url = https://github.com/vbabanin/specifications + branch = DRIVERS-2985 diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java new file mode 100644 index 00000000000..b58886d6fb2 --- /dev/null +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.reactivestreams.client.unified; + +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Collection; + +public class ClientMetadataTest extends UnifiedReactiveStreamsTest { + + private static Collection data() { + return getTestData("mongodb-handshake/tests/unified"); + } +} \ No newline at end of file diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index e798e0ea87d..5004e2a4e79 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -168,33 +168,6 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable } } - @Test - void shouldNotCloseExistingConnectionsToAppendMetadata() { - //given - MongoDriverInformation initialWrappingLibraryDriverInformation = MongoDriverInformation.builder() - .driverName("library") - .driverVersion("1.2") - .driverPlatform("Library Platform") - .build(); - - try (MongoClient mongoClient = createMongoClient(initialWrappingLibraryDriverInformation, getMongoClientSettingsBuilder() - .build())) { - - //TODO change get() to orElseThrow - BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); - BsonDocument driverInformation = clientMetadata.getDocument("driver"); - assertNotNull(driverInformation); - - //when - mongoClient.appendMetadata(initialWrappingLibraryDriverInformation); - - //then - assertThat(executePingAndCaptureMetadataHandshake(mongoClient)).isEmpty(); - assertFalse(connectionPoolListener.getEvents().stream().anyMatch(ConnectionClosedEvent.class::isInstance), - "Expected no connection closed events"); - } - } - // Not a prose test. Additional test for better coverage. @Test void shouldAppendProvidedMetadatDuringInitialization() { diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataTest.java new file mode 100644 index 00000000000..652d5a4059d --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientMetadataTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.client; + +import com.mongodb.client.unified.UnifiedSyncTest; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Collection; + +public class ClientMetadataTest extends UnifiedSyncTest { + + private static Collection data() { + return getTestData("mongodb-handshake/tests/unified"); + } +} diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java index 2c03bbba051..d3945221e14 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java @@ -17,6 +17,7 @@ package com.mongodb.client.unified; import com.mongodb.CursorType; +import com.mongodb.MongoDriverInformation; import com.mongodb.MongoNamespace; import com.mongodb.ReadConcern; import com.mongodb.ReadConcernLevel; @@ -39,6 +40,7 @@ import com.mongodb.client.ListIndexesIterable; import com.mongodb.client.ListSearchIndexesIterable; import com.mongodb.client.MongoChangeStreamCursor; +import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCluster; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; @@ -2217,6 +2219,24 @@ public OperationResult executeEstimatedDocumentCount(final BsonDocument operatio new BsonInt64(collection.estimatedDocumentCount(options))); } + public OperationResult executeUpdateClientMetadata(final BsonDocument operation) { + BsonDocument arguments = operation.getDocument("arguments", new BsonDocument()); + BsonDocument driverInfo = arguments.getDocument("driverInfoOptions"); + + MongoDriverInformation mongoDriverInformation = MongoDriverInformation.builder() + .driverVersion(driverInfo.getString("version").getValue()) + .driverName(driverInfo.getString("name").getValue()) + .driverPlatform(driverInfo.getString("platform").getValue()) + .build(); + + String clientId = operation.getString("object").getValue(); + MongoClient client = entities.getClient(clientId); + return resultOf(() -> { + client.appendMetadata(mongoDriverInformation); + return null; + }); + } + @NonNull private String createRandomEntityId() { return "random-entity-id" + uniqueIdGenerator.getAndIncrement(); diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java index 84eb40b4e29..9171f78b5b6 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java @@ -690,6 +690,8 @@ private OperationResult executeOperation(final UnifiedTestContext context, final return clientEncryptionHelper.executeEncrypt(operation); case "decrypt": return clientEncryptionHelper.executeDecrypt(operation); + case "appendMetadata": + return crudHelper.executeUpdateClientMetadata(operation); default: throw new UnsupportedOperationException("Unsupported test operation: " + name); } From d961447e2a9eaa1b4ceec74658414752619bb1f9 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 9 Jun 2025 22:17:32 -0700 Subject: [PATCH 25/26] Fix static checks. --- .../reactivestreams/client/unified/ClientMetadataTest.java | 2 +- .../com/mongodb/client/AbstractClientMetadataProseTest.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java index b58886d6fb2..6b0caf615bc 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/unified/ClientMetadataTest.java @@ -25,4 +25,4 @@ public class ClientMetadataTest extends UnifiedReactiveStreamsTest { private static Collection data() { return getTestData("mongodb-handshake/tests/unified"); } -} \ No newline at end of file +} diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java index 5004e2a4e79..9c552e31daf 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -19,7 +19,6 @@ import com.mongodb.MongoClientSettings; import com.mongodb.MongoDriverInformation; import com.mongodb.event.CommandStartedEvent; -import com.mongodb.event.ConnectionClosedEvent; import com.mongodb.internal.connection.InternalStreamConnection; import com.mongodb.internal.connection.TestCommandListener; import com.mongodb.internal.connection.TestConnectionPoolListener; @@ -42,8 +41,6 @@ import static com.mongodb.ClusterFixture.sleep; import static java.util.Optional.ofNullable; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeFalse; /** From 74d8558b1676482128d8ca1347950bc56bcb7be8 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 9 Jun 2025 22:36:51 -0700 Subject: [PATCH 26/26] Change specification submodule commit. --- driver-core/src/test/resources/specifications | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-core/src/test/resources/specifications b/driver-core/src/test/resources/specifications index f4c0bbdbf8a..43dea26ae75 160000 --- a/driver-core/src/test/resources/specifications +++ b/driver-core/src/test/resources/specifications @@ -1 +1 @@ -Subproject commit f4c0bbdbf8a8560580c947ca2c331794431a0c78 +Subproject commit 43dea26ae75c326b5c723cbf1f13c452350e2f99