From 9753f28a70f0249c23e89ee7790b55dc077489ad Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 6 May 2025 00:03:23 -0700 Subject: [PATCH 01/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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 From 31ef18ef42dbb3129dd0e71218cbf2a2d6e31465 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 16 Jun 2025 19:47:46 -0700 Subject: [PATCH 27/38] Revert specifications submodule URL to upstream repository. JAVA-5870 --- .gitmodules | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 58e84ab0966..a9ac62f04bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "specifications"] path = driver-core/src/test/resources/specifications - url = https://github.com/vbabanin/specifications - branch = DRIVERS-2985 + url = https://github.com/mongodb/specifications From 38ba5e6aeca0acb80a863f00e567b712fa263bdf Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 16 Jun 2025 19:50:21 -0700 Subject: [PATCH 28/38] Update specifications submodule commit hash. --- 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 43dea26ae75..d5adadb2f59 160000 --- a/driver-core/src/test/resources/specifications +++ b/driver-core/src/test/resources/specifications @@ -1 +1 @@ -Subproject commit 43dea26ae75c326b5c723cbf1f13c452350e2f99 +Subproject commit d5adadb2f59ba5c598bc46bc93b0f1edbea9381c From dab5451706ba23c13778f2c26296c13573a82280 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 16 Jun 2025 20:04:20 -0700 Subject: [PATCH 29/38] Remove duplicate import of CLIENT_METADATA in MultiServerClusterSpecification. --- .../internal/connection/MultiServerClusterSpecification.groovy | 1 - 1 file changed, 1 deletion(-) 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 11863174409..a3cf8104fd3 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 @@ -30,7 +30,6 @@ import spock.lang.Specification import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.ClusterFixture.OPERATION_CONTEXT -import static com.mongodb.ClusterFixture.CLIENT_METADATA import static com.mongodb.connection.ClusterConnectionMode.MULTIPLE import static com.mongodb.connection.ClusterType.REPLICA_SET import static com.mongodb.connection.ClusterType.SHARDED From b5c4c20672fac06789d7bb8e350a13699f228638 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 19 Jun 2025 21:06:22 -0700 Subject: [PATCH 30/38] Remove ClientMetadataHelper. --- .../internal/connection/ClientMetadata.java | 295 +++++++++++++++++- ...ProseTest.java => ClientMetadataTest.java} | 25 +- ...mConnectionInitializerSpecification.groovy | 1 - .../src/main/com/mongodb/MongoClient.java | 4 +- .../client/internal/MongoClientImpl.java | 4 +- .../client/internal/MongoClientImpl.java | 5 +- 6 files changed, 311 insertions(+), 23 deletions(-) rename driver-core/src/test/functional/com/mongodb/internal/connection/{ClientMetadataHelperProseTest.java => ClientMetadataTest.java} (94%) 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 e48327cd5a3..4575e82939f 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -17,21 +17,43 @@ package com.mongodb.internal.connection; import com.mongodb.MongoDriverInformation; +import com.mongodb.annotations.ThreadSafe; +import com.mongodb.internal.VisibleForTesting; +import com.mongodb.internal.build.MongoDriverVersion; import com.mongodb.lang.Nullable; +import org.bson.BsonBinaryWriter; import org.bson.BsonDocument; +import org.bson.BsonInt32; +import org.bson.BsonString; +import org.bson.BsonValue; +import org.bson.codecs.BsonDocumentCodec; +import org.bson.codecs.EncoderContext; +import org.bson.io.BasicOutputBuffer; +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.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; +import static com.mongodb.assertions.Assertions.isTrueArgument; import static com.mongodb.internal.Locks.withLock; -import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; -import static com.mongodb.internal.connection.ClientMetadataHelper.updateClientMetadataDocument; +import static com.mongodb.internal.connection.FaasEnvironment.getFaasEnvironment; +import static java.lang.String.format; +import static java.lang.System.getProperty; +import static java.nio.file.Paths.get; /** * Represents metadata of the current MongoClient. * *

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

*/ +@ThreadSafe public class ClientMetadata { + private static final String SEPARATOR = "|"; + private static final int MAXIMUM_CLIENT_METADATA_ENCODED_SIZE = 512; private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private BsonDocument clientMetadataBsonDocument; @@ -53,5 +75,272 @@ public void append(final MongoDriverInformation mongoDriverInformation) { this.clientMetadataBsonDocument = updateClientMetadataDocument(clientMetadataBsonDocument.clone(), mongoDriverInformation) ); } -} + private static BsonDocument createClientMetadataDocument(@Nullable final String applicationName, + @Nullable final MongoDriverInformation mongoDriverInformation) { + if (applicationName != null) { + isTrueArgument("applicationName UTF-8 encoding length <= 128", + applicationName.getBytes(StandardCharsets.UTF_8).length <= 128); + } + + // client fields are added in "preservation" order: + BsonDocument client = new BsonDocument(); + tryWithLimit(client, d -> putAtPath(d, "application.name", applicationName)); + MongoDriverInformation baseDriverInfor = getDriverInformation(null); + // required fields: + tryWithLimit(client, d -> { + putAtPath(d, "driver.name", listToString(baseDriverInfor.getDriverNames())); + putAtPath(d, "driver.version", listToString(baseDriverInfor.getDriverVersions())); + }); + tryWithLimit(client, d -> putAtPath(d, "os.type", getOperatingSystemType(getOperatingSystemName()))); + // full driver information: + MongoDriverInformation fullDriverInfo = getDriverInformation(mongoDriverInformation); + tryWithLimit(client, d -> { + putAtPath(d, "driver.name", listToString(fullDriverInfo.getDriverNames())); + putAtPath(d, "driver.version", listToString(fullDriverInfo.getDriverVersions())); + }); + + // optional fields: + FaasEnvironment faasEnvironment = getFaasEnvironment(); + ClientMetadata.ContainerRuntime containerRuntime = ClientMetadata.ContainerRuntime.determineExecutionContainer(); + ClientMetadata.Orchestrator orchestrator = ClientMetadata.Orchestrator.determineExecutionOrchestrator(); + + tryWithLimit(client, d -> putAtPath(d, "platform", listToString(baseDriverInfor.getDriverPlatforms()))); + tryWithLimit(client, d -> putAtPath(d, "platform", listToString(fullDriverInfo.getDriverPlatforms()))); + tryWithLimit(client, d -> putAtPath(d, "os.name", getOperatingSystemName())); + tryWithLimit(client, d -> putAtPath(d, "os.architecture", getProperty("os.arch", "unknown"))); + tryWithLimit(client, d -> putAtPath(d, "os.version", getProperty("os.version", "unknown"))); + + tryWithLimit(client, d -> putAtPath(d, "env.name", faasEnvironment.getName())); + tryWithLimit(client, d -> putAtPath(d, "env.timeout_sec", faasEnvironment.getTimeoutSec())); + tryWithLimit(client, d -> putAtPath(d, "env.memory_mb", faasEnvironment.getMemoryMb())); + tryWithLimit(client, d -> putAtPath(d, "env.region", faasEnvironment.getRegion())); + + tryWithLimit(client, d -> putAtPath(d, "env.container.runtime", containerRuntime.getName())); + tryWithLimit(client, d -> putAtPath(d, "env.container.orchestrator", orchestrator.getName())); + + return client; + } + + /** + * 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. + */ + private 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(currentDriverInformation.getString("name").getValue()); + updatedDriverNames.addAll(driverNamesToAppend); + + updatedDriverVersions.add(currentDriverInformation.getString("version").getValue()); + updatedDriverVersions.addAll(driverVersionsToAppend); + + updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); + updateDriverPlatforms.addAll(driverPlatformsToAppend); + + tryWithLimit(clientMetadataDocument, d -> { + putAtPath(d, "driver.name", listToString(updatedDriverNames)); + putAtPath(d, "driver.version", listToString(updatedDriverVersions)); + }); + tryWithLimit(clientMetadataDocument, d -> { + putAtPath(d, "platform", listToString(updateDriverPlatforms)); + }); + return clientMetadataDocument; + } + + + private static void putAtPath(final BsonDocument d, final String path, @Nullable final String value) { + if (value == null) { + return; + } + putAtPath(d, path, new BsonString(value)); + } + + private static void putAtPath(final BsonDocument d, final String path, @Nullable final Integer value) { + if (value == null) { + return; + } + putAtPath(d, path, new BsonInt32(value)); + } + + /** + * Assumes valid documents (or not set) on path. No-op if value is null. + */ + private static void putAtPath(final BsonDocument d, final String path, @Nullable final BsonValue value) { + if (value == null) { + return; + } + String[] split = path.split("\\.", 2); + String first = split[0]; + if (split.length == 1) { + d.append(first, value); + } else { + BsonDocument child; + if (d.containsKey(first)) { + child = d.getDocument(first); + } else { + child = new BsonDocument(); + d.append(first, child); + } + String rest = split[1]; + putAtPath(child, rest, value); + } + } + + private static void tryWithLimit(final BsonDocument document, final Consumer modifier) { + try { + BsonDocument temp = document.clone(); + modifier.accept(temp); + if (!clientMetadataDocumentTooLarge(temp)) { + modifier.accept(document); + } + } catch (Exception e) { + // do nothing. This could be a SecurityException, or any other issue while building the document + } + } + + static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { + BasicOutputBuffer buffer = new BasicOutputBuffer(MAXIMUM_CLIENT_METADATA_ENCODED_SIZE); + new BsonDocumentCodec().encode(new BsonBinaryWriter(buffer), document, EncoderContext.builder().build()); + return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE; + } + + private enum ContainerRuntime { + DOCKER("docker") { + @Override + boolean isCurrentRuntimeContainer() { + try { + return Files.exists(get(File.separator + ".dockerenv")); + } catch (Exception e) { + return false; + // NOOP. This could be a SecurityException. + } + } + }, + UNKNOWN(null); + + @Nullable + private final String name; + + ContainerRuntime(@Nullable final String name) { + this.name = name; + } + + @Nullable + public String getName() { + return name; + } + + boolean isCurrentRuntimeContainer() { + return false; + } + + static ClientMetadata.ContainerRuntime determineExecutionContainer() { + for (ClientMetadata.ContainerRuntime allegedContainer : ClientMetadata.ContainerRuntime.values()) { + if (allegedContainer.isCurrentRuntimeContainer()) { + return allegedContainer; + } + } + return UNKNOWN; + } + } + + private enum Orchestrator { + K8S("kubernetes") { + @Override + boolean isCurrentOrchestrator() { + return FaasEnvironment.getEnv("KUBERNETES_SERVICE_HOST") != null; + } + }, + UNKNOWN(null); + + @Nullable + private final String name; + + Orchestrator(@Nullable final String name) { + this.name = name; + } + + @Nullable + public String getName() { + return name; + } + + boolean isCurrentOrchestrator() { + return false; + } + + static ClientMetadata.Orchestrator determineExecutionOrchestrator() { + for (ClientMetadata.Orchestrator alledgedOrchestrator : ClientMetadata.Orchestrator.values()) { + if (alledgedOrchestrator.isCurrentOrchestrator()) { + return alledgedOrchestrator; + } + } + return UNKNOWN; + } + } + + static MongoDriverInformation getDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) { + MongoDriverInformation.Builder builder = mongoDriverInformation != null ? MongoDriverInformation.builder(mongoDriverInformation) + : MongoDriverInformation.builder(); + return builder + .driverName(MongoDriverVersion.NAME) + .driverVersion(MongoDriverVersion.VERSION) + .driverPlatform(format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"), + getProperty("java.runtime.version", "unknown-version"))) + .build(); + } + + private static String listToString(final List listOfStrings) { + StringBuilder stringBuilder = new StringBuilder(); + int i = 0; + for (String val : listOfStrings) { + if (i > 0) { + stringBuilder.append(SEPARATOR); + } + stringBuilder.append(val); + i++; + } + return stringBuilder.toString(); + } + + @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE) + public static String getOperatingSystemType(final String operatingSystemName) { + if (nameStartsWith(operatingSystemName, "linux")) { + return "Linux"; + } else if (nameStartsWith(operatingSystemName, "mac")) { + return "Darwin"; + } else if (nameStartsWith(operatingSystemName, "windows")) { + return "Windows"; + } else if (nameStartsWith(operatingSystemName, "hp-ux", "aix", "irix", "solaris", "sunos")) { + return "Unix"; + } else { + return "unknown"; + } + } + + private static String getOperatingSystemName() { + return getProperty("os.name", "unknown"); + } + + private static boolean nameStartsWith(final String name, final String... prefixes) { + for (String prefix : prefixes) { + if (name.toLowerCase().startsWith(prefix.toLowerCase())) { + return true; + } + } + return false; + } +} diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java similarity index 94% rename from driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java rename to driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java index ae7bc9e5e7c..60489736874 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataHelperProseTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java @@ -44,9 +44,7 @@ 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 com.mongodb.internal.connection.ClientMetadata.getOperatingSystemType; import static java.util.Optional.ofNullable; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -58,7 +56,7 @@ *

* NOTE: This class also contains tests that aren't categorized as Prose tests. */ -public class ClientMetadataHelperProseTest { +public class ClientMetadataTest { private static final String APP_NAME = "app name"; @Test @@ -264,7 +262,7 @@ public void testLimitForDriverVersion() { BsonDocument expectedBase = createExpectedClientMetadataDocument(APP_NAME); expected.put("driver", expectedBase.get("driver")); - BsonDocument actual = createClientMetadataDocument(APP_NAME, driverInfo); + BsonDocument actual = new ClientMetadata(APP_NAME, driverInfo).getBsonDocument(); assertEquals(expected, actual); } @@ -280,7 +278,7 @@ public void testLimitForPlatform() { BsonDocument expectedBase = createExpectedClientMetadataDocument(APP_NAME); expected.put("platform", expectedBase.get("platform")); - BsonDocument actual = createClientMetadataDocument(APP_NAME, driverInfo); + BsonDocument actual = new ClientMetadata(APP_NAME, driverInfo).getBsonDocument(); assertEquals(expected, actual); } @@ -300,14 +298,14 @@ public void testLimitForOsName() { @Test public void testApplicationNameUnderLimit() { String applicationName = repeat(126, "a") + "\u00A0"; - BsonDocument client = createClientMetadataDocument(applicationName, null); + BsonDocument client = new ClientMetadata(applicationName, null).getBsonDocument(); assertEquals(applicationName, client.getDocument("application").getString("name").getValue()); } @Test public void testApplicationNameOverLimit() { String applicationName = repeat(127, "a") + "\u00A0"; - assertThrows(IllegalArgumentException.class, () -> createClientMetadataDocument(applicationName, null)); + assertThrows(IllegalArgumentException.class, () -> new ClientMetadata(applicationName, null)); } @ParameterizedTest @@ -319,9 +317,10 @@ public void testApplicationNameOverLimit() { }) public void testCreateClientMetadataDocument(@Nullable final String appName, final boolean hasDriverInfo) { MongoDriverInformation driverInformation = hasDriverInfo ? createDriverInformation() : null; + ClientMetadata clientMetadata = new ClientMetadata(appName, driverInformation); assertEquals( createExpectedClientMetadataDocument(appName, driverInformation), - createClientMetadataDocument(appName, driverInformation)); + clientMetadata.getBsonDocument()); } public static java.util.stream.Stream provideDriverInformation() { @@ -347,7 +346,8 @@ void testUpdateClientMetadataDocument(@Nullable final String driverVersion, .driverPlatform("Scala 2.10 / Spark 2.0.0") .build(); - BsonDocument initialClientMetadataDocument = createClientMetadataDocument(null, initialDriverInformation); + ClientMetadata clientMetadata = new ClientMetadata(null, initialDriverInformation); + BsonDocument initialClientMetadataDocument = clientMetadata.getBsonDocument(); assertEquals( createExpectedClientMetadataDocument(null, initialDriverInformation), initialClientMetadataDocument); @@ -367,7 +367,8 @@ void testUpdateClientMetadataDocument(@Nullable final String driverVersion, .build(); //when - BsonDocument updatedClientMetadata = updateClientMetadataDocument(initialClientMetadataDocument.clone(), metadataToAppend); + clientMetadata.append(metadataToAppend); + BsonDocument updatedClientMetadata = clientMetadata.getBsonDocument(); //then assertEquals( @@ -401,7 +402,7 @@ private void performHello() { } private BsonDocument createActualClientMetadataDocument() { - return createClientMetadataDocument(APP_NAME, null); + return new ClientMetadata(APP_NAME, null).getBsonDocument(); } private static MongoDriverInformation createDriverInformation() { diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy index 156499797c2..42f815a80db 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy @@ -44,7 +44,6 @@ import static com.mongodb.MongoCredential.createPlainCredential import static com.mongodb.MongoCredential.createScramSha1Credential import static com.mongodb.MongoCredential.createScramSha256Credential import static com.mongodb.connection.ClusterConnectionMode.SINGLE -import static com.mongodb.internal.connection.ClientMetadataHelperProseTest.createExpectedClientMetadataDocument import static com.mongodb.internal.connection.MessageHelper.LEGACY_HELLO import static com.mongodb.internal.connection.MessageHelper.buildSuccessfulReply import static com.mongodb.internal.connection.MessageHelper.decodeCommand diff --git a/driver-legacy/src/main/com/mongodb/MongoClient.java b/driver-legacy/src/main/com/mongodb/MongoClient.java index 21323a40604..31da4c1b9ef 100644 --- a/driver-legacy/src/main/com/mongodb/MongoClient.java +++ b/driver-legacy/src/main/com/mongodb/MongoClient.java @@ -66,7 +66,6 @@ import java.util.stream.Collectors; import static com.mongodb.assertions.Assertions.notNull; -import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.connection.ServerAddressHelper.createServerAddress; import static com.mongodb.internal.connection.ServerAddressHelper.getInetAddressResolver; import static com.mongodb.internal.connection.StreamFactoryHelper.getSyncStreamFactoryFactory; @@ -266,7 +265,8 @@ private MongoClient(final MongoClientSettings settings, this.options = options != null ? options : MongoClientOptions.builder(settings).build(); cursorCleaningService = this.options.isCursorFinalizerEnabled() ? createCursorCleaningService() : null; this.closed = new AtomicBoolean(); - BsonDocument clientMetadataDocument = createClientMetadataDocument(settings.getApplicationName(), mongoDriverInformation); + + BsonDocument clientMetadataDocument = delegate.getCluster().getClientMetadata().getBsonDocument(); LOGGER.info(format("MongoClient with metadata %s created with settings %s", clientMetadataDocument.toJson(), settings)); } 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 473ec0915d0..18c28418617 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 @@ -54,7 +54,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.mongodb.assertions.Assertions.notNull; -import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static java.lang.String.format; import static org.bson.codecs.configuration.CodecRegistries.withUuidRepresentation; @@ -117,7 +116,8 @@ private MongoClientImpl(final MongoClientSettings settings, final MongoDriverInf this.externalResourceCloser = externalResourceCloser; this.settings = settings; this.closed = new AtomicBoolean(); - BsonDocument clientMetadataDocument = createClientMetadataDocument(settings.getApplicationName(), mongoDriverInformation); + + BsonDocument clientMetadataDocument = delegate.getCluster().getClientMetadata().getBsonDocument(); LOGGER.info(format("MongoClient with metadata %s created with settings %s", clientMetadataDocument.toJson(), settings)); } 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 47e3266cbf1..00667507e17 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -58,7 +58,6 @@ import static com.mongodb.assertions.Assertions.notNull; import static com.mongodb.client.internal.Crypts.createCrypt; -import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; import static com.mongodb.internal.event.EventListenerHelper.getCommandListener; import static java.lang.String.format; import static org.bson.codecs.configuration.CodecRegistries.withUuidRepresentation; @@ -84,7 +83,7 @@ public MongoClientImpl(final Cluster cluster, private MongoClientImpl(final Cluster cluster, final MongoDriverInformation mongoDriverInformation, - final MongoClientSettings settings, + final MongoClientSettings settings, @Nullable final AutoCloseable externalResourceCloser, @Nullable final OperationExecutor operationExecutor) { @@ -106,8 +105,8 @@ private MongoClientImpl(final Cluster cluster, new ServerSessionPool(cluster, TimeoutSettings.create(settings), settings.getServerApi()), TimeoutSettings.create(settings), settings.getUuidRepresentation(), settings.getWriteConcern()); this.closed = new AtomicBoolean(); - BsonDocument clientMetadataDocument = createClientMetadataDocument(settings.getApplicationName(), mongoDriverInformation); + BsonDocument clientMetadataDocument = delegate.getCluster().getClientMetadata().getBsonDocument(); LOGGER.info(format("MongoClient with metadata %s created with settings %s", clientMetadataDocument.toJson(), settings)); } From b6b4d67c64bf25502829ed8f4cd6ac9e05780f20 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 19 Jun 2025 23:16:00 -0700 Subject: [PATCH 31/38] Update native-image.properties. --- .../connection/ClientMetadataHelper.java | 323 ------------------ .../native-image/native-image.properties | 2 +- 2 files changed, 1 insertion(+), 324 deletions(-) delete mode 100644 driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java diff --git a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java deleted file mode 100644 index 2675e0f8efd..00000000000 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * 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.internal.VisibleForTesting; -import com.mongodb.internal.build.MongoDriverVersion; -import com.mongodb.lang.Nullable; -import org.bson.BsonBinaryWriter; -import org.bson.BsonDocument; -import org.bson.BsonInt32; -import org.bson.BsonString; -import org.bson.BsonValue; -import org.bson.codecs.BsonDocumentCodec; -import org.bson.codecs.EncoderContext; -import org.bson.io.BasicOutputBuffer; - -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; - -import static com.mongodb.assertions.Assertions.isTrueArgument; -import static com.mongodb.internal.connection.FaasEnvironment.getFaasEnvironment; -import static java.lang.String.format; -import static java.lang.System.getProperty; -import static java.nio.file.Paths.get; - -/** - *

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

- */ -public final class ClientMetadataHelper { - private static final String SEPARATOR = "|"; - - private static final int MAXIMUM_CLIENT_METADATA_ENCODED_SIZE = 512; - - @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE) - static String getOperatingSystemType(final String operatingSystemName) { - if (nameStartsWith(operatingSystemName, "linux")) { - return "Linux"; - } else if (nameStartsWith(operatingSystemName, "mac")) { - return "Darwin"; - } else if (nameStartsWith(operatingSystemName, "windows")) { - return "Windows"; - } else if (nameStartsWith(operatingSystemName, "hp-ux", "aix", "irix", "solaris", "sunos")) { - return "Unix"; - } else { - return "unknown"; - } - } - - private static String getOperatingSystemName() { - return getProperty("os.name", "unknown"); - } - - private static boolean nameStartsWith(final String name, final String... prefixes) { - for (String prefix : prefixes) { - if (name.toLowerCase().startsWith(prefix.toLowerCase())) { - return true; - } - } - return false; - } - - public static BsonDocument createClientMetadataDocument(@Nullable final String applicationName, - @Nullable final MongoDriverInformation mongoDriverInformation) { - if (applicationName != null) { - isTrueArgument("applicationName UTF-8 encoding length <= 128", - applicationName.getBytes(StandardCharsets.UTF_8).length <= 128); - } - - // client fields are added in "preservation" order: - BsonDocument client = new BsonDocument(); - tryWithLimit(client, d -> putAtPath(d, "application.name", applicationName)); - MongoDriverInformation baseDriverInfor = getDriverInformation(null); - // required fields: - tryWithLimit(client, d -> { - putAtPath(d, "driver.name", listToString(baseDriverInfor.getDriverNames())); - putAtPath(d, "driver.version", listToString(baseDriverInfor.getDriverVersions())); - }); - tryWithLimit(client, d -> putAtPath(d, "os.type", getOperatingSystemType(getOperatingSystemName()))); - // full driver information: - MongoDriverInformation fullDriverInfo = getDriverInformation(mongoDriverInformation); - tryWithLimit(client, d -> { - putAtPath(d, "driver.name", listToString(fullDriverInfo.getDriverNames())); - putAtPath(d, "driver.version", listToString(fullDriverInfo.getDriverVersions())); - }); - - // optional fields: - FaasEnvironment faasEnvironment = getFaasEnvironment(); - ContainerRuntime containerRuntime = ContainerRuntime.determineExecutionContainer(); - Orchestrator orchestrator = Orchestrator.determineExecutionOrchestrator(); - - tryWithLimit(client, d -> putAtPath(d, "platform", listToString(baseDriverInfor.getDriverPlatforms()))); - tryWithLimit(client, d -> putAtPath(d, "platform", listToString(fullDriverInfo.getDriverPlatforms()))); - tryWithLimit(client, d -> putAtPath(d, "os.name", getOperatingSystemName())); - tryWithLimit(client, d -> putAtPath(d, "os.architecture", getProperty("os.arch", "unknown"))); - tryWithLimit(client, d -> putAtPath(d, "os.version", getProperty("os.version", "unknown"))); - - tryWithLimit(client, d -> putAtPath(d, "env.name", faasEnvironment.getName())); - tryWithLimit(client, d -> putAtPath(d, "env.timeout_sec", faasEnvironment.getTimeoutSec())); - tryWithLimit(client, d -> putAtPath(d, "env.memory_mb", faasEnvironment.getMemoryMb())); - tryWithLimit(client, d -> putAtPath(d, "env.region", faasEnvironment.getRegion())); - - tryWithLimit(client, d -> putAtPath(d, "env.container.runtime", containerRuntime.getName())); - tryWithLimit(client, d -> putAtPath(d, "env.container.orchestrator", orchestrator.getName())); - - return client; - } - - - private static void putAtPath(final BsonDocument d, final String path, @Nullable final String value) { - if (value == null) { - return; - } - putAtPath(d, path, new BsonString(value)); - } - - private static void putAtPath(final BsonDocument d, final String path, @Nullable final Integer value) { - if (value == null) { - return; - } - putAtPath(d, path, new BsonInt32(value)); - } - - /** - * Assumes valid documents (or not set) on path. No-op if value is null. - */ - private static void putAtPath(final BsonDocument d, final String path, @Nullable final BsonValue value) { - if (value == null) { - return; - } - String[] split = path.split("\\.", 2); - String first = split[0]; - if (split.length == 1) { - d.append(first, value); - } else { - BsonDocument child; - if (d.containsKey(first)) { - child = d.getDocument(first); - } else { - child = new BsonDocument(); - d.append(first, child); - } - String rest = split[1]; - putAtPath(child, rest, value); - } - } - - private static void tryWithLimit(final BsonDocument document, final Consumer modifier) { - try { - BsonDocument temp = document.clone(); - modifier.accept(temp); - if (!clientMetadataDocumentTooLarge(temp)) { - modifier.accept(document); - } - } catch (Exception e) { - // do nothing. This could be a SecurityException, or any other issue while building the document - } - } - - static boolean clientMetadataDocumentTooLarge(final BsonDocument document) { - BasicOutputBuffer buffer = new BasicOutputBuffer(MAXIMUM_CLIENT_METADATA_ENCODED_SIZE); - 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 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(currentDriverInformation.getString("name").getValue()); - updatedDriverNames.addAll(driverNamesToAppend); - - updatedDriverVersions.add(currentDriverInformation.getString("version").getValue()); - updatedDriverVersions.addAll(driverVersionsToAppend); - - updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); - updateDriverPlatforms.addAll(driverPlatformsToAppend); - - tryWithLimit(clientMetadataDocument, d -> { - putAtPath(d, "driver.name", listToString(updatedDriverNames)); - putAtPath(d, "driver.version", listToString(updatedDriverVersions)); - }); - tryWithLimit(clientMetadataDocument, d -> { - putAtPath(d, "platform", listToString(updateDriverPlatforms)); - }); - return clientMetadataDocument; - } - - public enum ContainerRuntime { - DOCKER("docker") { - @Override - boolean isCurrentRuntimeContainer() { - try { - return Files.exists(get(File.separator + ".dockerenv")); - } catch (Exception e) { - return false; - // NOOP. This could be a SecurityException. - } - } - }, - UNKNOWN(null); - - @Nullable - private final String name; - - ContainerRuntime(@Nullable final String name) { - this.name = name; - } - - @Nullable - public String getName() { - return name; - } - - boolean isCurrentRuntimeContainer() { - return false; - } - - static ContainerRuntime determineExecutionContainer() { - for (ContainerRuntime allegedContainer : ContainerRuntime.values()) { - if (allegedContainer.isCurrentRuntimeContainer()) { - return allegedContainer; - } - } - return UNKNOWN; - } - } - - private enum Orchestrator { - K8S("kubernetes") { - @Override - boolean isCurrentOrchestrator() { - return FaasEnvironment.getEnv("KUBERNETES_SERVICE_HOST") != null; - } - }, - UNKNOWN(null); - - @Nullable - private final String name; - - Orchestrator(@Nullable final String name) { - this.name = name; - } - - @Nullable - public String getName() { - return name; - } - - boolean isCurrentOrchestrator() { - return false; - } - - static Orchestrator determineExecutionOrchestrator() { - for (Orchestrator alledgedOrchestrator : Orchestrator.values()) { - if (alledgedOrchestrator.isCurrentOrchestrator()) { - return alledgedOrchestrator; - } - } - return UNKNOWN; - } - } - - static MongoDriverInformation getDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) { - MongoDriverInformation.Builder builder = mongoDriverInformation != null ? MongoDriverInformation.builder(mongoDriverInformation) - : MongoDriverInformation.builder(); - return builder - .driverName(MongoDriverVersion.NAME) - .driverVersion(MongoDriverVersion.VERSION) - .driverPlatform(format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"), - getProperty("java.runtime.version", "unknown-version"))) - .build(); - } - - private static String listToString(final List listOfStrings) { - StringBuilder stringBuilder = new StringBuilder(); - int i = 0; - for (String val : listOfStrings) { - if (i > 0) { - stringBuilder.append(SEPARATOR); - } - stringBuilder.append(val); - i++; - } - return stringBuilder.toString(); - } - - private ClientMetadataHelper() { - } -} diff --git a/driver-core/src/main/resources/META-INF/native-image/native-image.properties b/driver-core/src/main/resources/META-INF/native-image/native-image.properties index 49541a06e0e..6de9c4d8765 100644 --- a/driver-core/src/main/resources/META-INF/native-image/native-image.properties +++ b/driver-core/src/main/resources/META-INF/native-image/native-image.properties @@ -17,6 +17,6 @@ Args =\ --initialize-at-run-time=\ com.mongodb.UnixServerAddress,\ com.mongodb.internal.connection.SnappyCompressor,\ - com.mongodb.internal.connection.ClientMetadataHelper,\ + com.mongodb.internal.connection.ClientMetadata,\ com.mongodb.internal.connection.ServerAddressHelper,\ com.mongodb.internal.dns.DefaultDnsResolver From d43972d13999350ade2afbdc70c19ca813a6d83b Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 24 Jun 2025 00:10:08 -0700 Subject: [PATCH 32/38] Refactor tests to use JUnit assertions and remove unused dependencies. --- .../conventions/testing-base.gradle.kts | 4 -- .../AbstractClientMetadataProseTest.java | 38 ++++++++++--------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts b/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts index da4a9b10866..8aa6d25a5fd 100644 --- a/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts +++ b/buildSrc/src/main/kotlin/conventions/testing-base.gradle.kts @@ -105,7 +105,3 @@ testlogger { showFailedStandardStreams = true logLevel = LogLevel.LIFECYCLE } - -dependencies { - testImplementation(libs.assertj) -} 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 9c552e31daf..4d796c9816c 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -39,8 +39,9 @@ import static com.mongodb.ClusterFixture.isAuthenticated; import static com.mongodb.ClusterFixture.isLoadBalanced; import static com.mongodb.ClusterFixture.sleep; +import static com.mongodb.assertions.Assertions.assertTrue; import static java.util.Optional.ofNullable; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeFalse; /** @@ -115,12 +116,13 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization(@Nullable fina 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")); + assertEquals(updatedDriverInformation.getString("name").getValue(), expectedDriverName); + assertTrue(updatedDriverInformation.getString("version").getValue().endsWith(expectedDriverVersion)); + assertTrue(updatedClientMetadata.getString("platform").getValue().endsWith(expectedDriverPlatform)); + + assertEquals( + withRemovedKeys(updatedClientMetadata, "driver", "platform"), + withRemovedKeys(initialClientMetadata, "driver", "platform")); } } @@ -156,12 +158,13 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable 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")); + assertEquals(updatedDriverInformation.getString("name").getValue(), expectedDriverName); + assertTrue(updatedDriverInformation.getString("version").getValue().endsWith(expectedDriverVersion)); + assertTrue(updatedClientMetadata.getString("platform").getValue().endsWith(expectedDriverPlatform)); + + assertEquals( + withRemovedKeys(updatedClientMetadata, "driver", "platform"), + withRemovedKeys(initialClientMetadata, "driver", "platform")); } } @@ -184,9 +187,9 @@ void shouldAppendProvidedMetadatDuringInitialization() { 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"); + assertTrue(driverInformation.get("name").asString().getValue().endsWith("|library")); + assertTrue(driverInformation.get("version").asString().getValue().endsWith("|1.2")); + assertTrue(clientMetadata.get("platform").asString().getValue().endsWith("|Library Platform")); } } @@ -232,5 +235,4 @@ private static void updateClientMetadata(@Nullable final String driverVersion, ofNullable(driverPlatform).ifPresent(builder::driverPlatform); mongoClient.appendMetadata(builder.build()); } -} - +} \ No newline at end of file From 73bdbd85d30136a05ebbdeca107c9c01b0a6a676 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 24 Jun 2025 14:04:21 -0700 Subject: [PATCH 33/38] Fix test. --- .../InternalStreamConnectionInitializerSpecification.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy index 42f815a80db..1d44f8dde46 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/InternalStreamConnectionInitializerSpecification.groovy @@ -224,7 +224,7 @@ class InternalStreamConnectionInitializerSpecification extends Specification { decodeCommand(internalConnection.getSent()[0]) == expectedHelloCommandDocument where: - [clientMetadataDocument, async] << [[createExpectedClientMetadataDocument('appName'), null], + [clientMetadataDocument, async] << [[ClientMetadataTest.createExpectedClientMetadataDocument('appName'), null], [true, false]].combinations() } From 66a59131c2309c55c225eb5054a5f946ba7b45b2 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Tue, 24 Jun 2025 23:21:51 -0700 Subject: [PATCH 34/38] Add internal driver information. --- .../internal/connection/ClientMetadata.java | 154 ++++++++++-------- .../AbstractClientMetadataProseTest.java | 2 +- 2 files changed, 91 insertions(+), 65 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 4575e82939f..c83a32ce4d4 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java @@ -48,6 +48,8 @@ /** * Represents metadata of the current MongoClient. * + * Metadata is used to identify the client in the server logs and metrics. + * *

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

*/ @ThreadSafe @@ -55,11 +57,18 @@ public class ClientMetadata { private static final String SEPARATOR = "|"; private static final int MAXIMUM_CLIENT_METADATA_ENCODED_SIZE = 512; private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final String applicationName; private BsonDocument clientMetadataBsonDocument; + private DriverInformation driverInformation; public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { + this.applicationName = applicationName; withLock(readWriteLock.writeLock(), () -> { - this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, mongoDriverInformation); + this.driverInformation = DriverInformation.from( + mongoDriverInformation.getDriverNames(), + mongoDriverInformation.getDriverVersions(), + mongoDriverInformation.getDriverPlatforms()); + this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, driverInformation); }); } @@ -70,14 +79,18 @@ public BsonDocument getBsonDocument() { return withLock(readWriteLock.readLock(), () -> clientMetadataBsonDocument); } - public void append(final MongoDriverInformation mongoDriverInformation) { - withLock(readWriteLock.writeLock(), () -> - this.clientMetadataBsonDocument = updateClientMetadataDocument(clientMetadataBsonDocument.clone(), mongoDriverInformation) - ); + public void append(final MongoDriverInformation mongoDriverInformationToAppend) { + withLock(readWriteLock.writeLock(), () -> { + this.driverInformation.append( + mongoDriverInformationToAppend.getDriverNames(), + mongoDriverInformationToAppend.getDriverVersions(), + mongoDriverInformationToAppend.getDriverPlatforms()); + this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, driverInformation); + }); } private static BsonDocument createClientMetadataDocument(@Nullable final String applicationName, - @Nullable final MongoDriverInformation mongoDriverInformation) { + final DriverInformation driverInformation) { if (applicationName != null) { isTrueArgument("applicationName UTF-8 encoding length <= 128", applicationName.getBytes(StandardCharsets.UTF_8).length <= 128); @@ -86,18 +99,17 @@ private static BsonDocument createClientMetadataDocument(@Nullable final String // client fields are added in "preservation" order: BsonDocument client = new BsonDocument(); tryWithLimit(client, d -> putAtPath(d, "application.name", applicationName)); - MongoDriverInformation baseDriverInfor = getDriverInformation(null); + // required fields: tryWithLimit(client, d -> { - putAtPath(d, "driver.name", listToString(baseDriverInfor.getDriverNames())); - putAtPath(d, "driver.version", listToString(baseDriverInfor.getDriverVersions())); + putAtPath(d, "driver.name", driverInformation.getInitialDriverName()); + putAtPath(d, "driver.version", driverInformation.getInitialDriverVersion()); }); tryWithLimit(client, d -> putAtPath(d, "os.type", getOperatingSystemType(getOperatingSystemName()))); // full driver information: - MongoDriverInformation fullDriverInfo = getDriverInformation(mongoDriverInformation); tryWithLimit(client, d -> { - putAtPath(d, "driver.name", listToString(fullDriverInfo.getDriverNames())); - putAtPath(d, "driver.version", listToString(fullDriverInfo.getDriverVersions())); + putAtPath(d, "driver.name", listToString(driverInformation.getAllDriverNames())); + putAtPath(d, "driver.version", listToString(driverInformation.getAllDriverVersions())); }); // optional fields: @@ -105,8 +117,8 @@ private static BsonDocument createClientMetadataDocument(@Nullable final String ClientMetadata.ContainerRuntime containerRuntime = ClientMetadata.ContainerRuntime.determineExecutionContainer(); ClientMetadata.Orchestrator orchestrator = ClientMetadata.Orchestrator.determineExecutionOrchestrator(); - tryWithLimit(client, d -> putAtPath(d, "platform", listToString(baseDriverInfor.getDriverPlatforms()))); - tryWithLimit(client, d -> putAtPath(d, "platform", listToString(fullDriverInfo.getDriverPlatforms()))); + tryWithLimit(client, d -> putAtPath(d, "platform", driverInformation.getInitialDriverPlatform())); + tryWithLimit(client, d -> putAtPath(d, "platform", listToString(driverInformation.getAllDriverPlatforms()))); tryWithLimit(client, d -> putAtPath(d, "os.name", getOperatingSystemName())); tryWithLimit(client, d -> putAtPath(d, "os.architecture", getProperty("os.arch", "unknown"))); tryWithLimit(client, d -> putAtPath(d, "os.version", getProperty("os.version", "unknown"))); @@ -122,45 +134,6 @@ private static BsonDocument createClientMetadataDocument(@Nullable final String return client; } - /** - * 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. - */ - private 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(currentDriverInformation.getString("name").getValue()); - updatedDriverNames.addAll(driverNamesToAppend); - - updatedDriverVersions.add(currentDriverInformation.getString("version").getValue()); - updatedDriverVersions.addAll(driverVersionsToAppend); - - updateDriverPlatforms.add(clientMetadataDocument.getString("platform").getValue()); - updateDriverPlatforms.addAll(driverPlatformsToAppend); - - tryWithLimit(clientMetadataDocument, d -> { - putAtPath(d, "driver.name", listToString(updatedDriverNames)); - putAtPath(d, "driver.version", listToString(updatedDriverVersions)); - }); - tryWithLimit(clientMetadataDocument, d -> { - putAtPath(d, "platform", listToString(updateDriverPlatforms)); - }); - return clientMetadataDocument; - } - - private static void putAtPath(final BsonDocument d, final String path, @Nullable final String value) { if (value == null) { return; @@ -292,17 +265,6 @@ static ClientMetadata.Orchestrator determineExecutionOrchestrator() { } } - static MongoDriverInformation getDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) { - MongoDriverInformation.Builder builder = mongoDriverInformation != null ? MongoDriverInformation.builder(mongoDriverInformation) - : MongoDriverInformation.builder(); - return builder - .driverName(MongoDriverVersion.NAME) - .driverVersion(MongoDriverVersion.VERSION) - .driverPlatform(format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"), - getProperty("java.runtime.version", "unknown-version"))) - .build(); - } - private static String listToString(final List listOfStrings) { StringBuilder stringBuilder = new StringBuilder(); int i = 0; @@ -343,4 +305,68 @@ private static boolean nameStartsWith(final String name, final String... prefixe } return false; } + + /** + * Holds driver information of client.driver field + * in {@link ClientMetadata#clientMetadataBsonDocument}. + */ + private static class DriverInformation { + private final List driverNames; + private final List driverVersions; + private final List driverPlatforms; + private final String initialPlatform; + + DriverInformation() { + this.driverNames = new ArrayList<>(); + driverNames.add(MongoDriverVersion.NAME); + + this.driverVersions = new ArrayList<>(); + driverVersions.add(MongoDriverVersion.VERSION); + + this.initialPlatform = format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"), + getProperty("java.runtime.version", "unknown-version")); + this.driverPlatforms = new ArrayList<>(); + driverPlatforms.add(initialPlatform); + } + + static DriverInformation from(final List driverNames, + final List driverVersions, + final List driverPlatforms) { + DriverInformation driverInformation = new DriverInformation(); + return driverInformation.append(driverNames, driverVersions, driverPlatforms); + } + + DriverInformation append(final List driverNames, + final List driverVersions, + final List driverPlatforms) { + this.driverNames.addAll(driverNames); + this.driverVersions.addAll(driverVersions); + this.driverPlatforms.addAll(driverPlatforms); + return this; + } + + public String getInitialDriverPlatform() { + return initialPlatform; + } + + public String getInitialDriverName() { + return MongoDriverVersion.NAME; + } + + public String getInitialDriverVersion() { + return MongoDriverVersion.VERSION; + } + + public List getAllDriverNames() { + return driverNames; + } + + public List getAllDriverVersions() { + return driverVersions; + } + + public List getAllDriverPlatforms() { + return driverPlatforms; + } + } } 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 4d796c9816c..28465633016 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -235,4 +235,4 @@ private static void updateClientMetadata(@Nullable final String driverVersion, ofNullable(driverPlatform).ifPresent(builder::driverPlatform); mongoClient.appendMetadata(builder.build()); } -} \ No newline at end of file +} From 26246f046b8133b19b213475d096c9abea07f8ce Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 25 Jun 2025 13:41:38 -0700 Subject: [PATCH 35/38] Fix tests. --- .../mongodb/MongoClientSpecification.groovy | 7 ++++++- .../client/internal/MongoClientImplTest.java | 20 +++++++++---------- .../client/internal/MongoClientImpl.java | 4 +++- .../com/mongodb/client/MongoClientTest.java | 7 ++++++- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/driver-legacy/src/test/unit/com/mongodb/MongoClientSpecification.groovy b/driver-legacy/src/test/unit/com/mongodb/MongoClientSpecification.groovy index 0816dc83a87..1389a41c760 100644 --- a/driver-legacy/src/test/unit/com/mongodb/MongoClientSpecification.groovy +++ b/driver-legacy/src/test/unit/com/mongodb/MongoClientSpecification.groovy @@ -21,6 +21,7 @@ import com.mongodb.client.internal.MongoDatabaseImpl import com.mongodb.client.internal.TestOperationExecutor import com.mongodb.client.model.geojson.MultiPolygon import com.mongodb.connection.ClusterSettings +import com.mongodb.internal.connection.ClientMetadata import com.mongodb.internal.connection.Cluster import org.bson.BsonDocument import org.bson.Document @@ -309,7 +310,11 @@ class MongoClientSpecification extends Specification { def 'should validate the ChangeStreamIterable pipeline data correctly'() { given: def executor = new TestOperationExecutor([]) - def client = new MongoClientImpl(Stub(Cluster), null, MongoClientSettings.builder().build(), null, executor) + + def clusterStub = Stub(Cluster) + clusterStub.getClientMetadata() >> new ClientMetadata("test", MongoDriverInformation.builder().build()) + + def client = new MongoClientImpl(clusterStub, null, MongoClientSettings.builder().build(), null, executor) when: client.watch((Class) null) diff --git a/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/MongoClientImplTest.java b/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/MongoClientImplTest.java index 1eb42c647d7..c192ae17896 100644 --- a/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/MongoClientImplTest.java +++ b/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/MongoClientImplTest.java @@ -20,12 +20,11 @@ import com.mongodb.MongoClientSettings; import com.mongodb.MongoDriverInformation; import com.mongodb.ReadConcern; -import com.mongodb.ServerAddress; import com.mongodb.TransactionOptions; -import com.mongodb.connection.ServerConnectionState; -import com.mongodb.connection.ServerDescription; import com.mongodb.internal.client.model.changestream.ChangeStreamLevel; +import com.mongodb.internal.connection.ClientMetadata; import com.mongodb.internal.connection.Cluster; +import com.mongodb.internal.mockito.MongoMockito; import com.mongodb.internal.session.ServerSessionPool; import com.mongodb.reactivestreams.client.ChangeStreamPublisher; import com.mongodb.reactivestreams.client.ClientSession; @@ -44,6 +43,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MongoClientImplTest extends TestHelper { @@ -178,13 +178,6 @@ void testWatch() { @Test void testStartSession() { - ServerDescription serverDescription = ServerDescription.builder() - .address(new ServerAddress()) - .state(ServerConnectionState.CONNECTED) - .maxWireVersion(8) - .build(); - - MongoClientImpl mongoClient = createMongoClient(); ServerSessionPool serverSessionPool = mock(ServerSessionPool.class); ClientSessionHelper clientSessionHelper = new ClientSessionHelper(mongoClient, serverSessionPool); @@ -209,7 +202,12 @@ void testStartSession() { } private MongoClientImpl createMongoClient() { + MongoDriverInformation mongoDriverInformation = MongoDriverInformation.builder().driverName("reactive-streams").build(); + Cluster mock = MongoMockito.mock(Cluster.class, cluster -> { + when(cluster.getClientMetadata()) + .thenReturn(new ClientMetadata("test", mongoDriverInformation)); + }); return new MongoClientImpl(MongoClientSettings.builder().build(), - MongoDriverInformation.builder().driverName("reactive-streams").build(), mock(Cluster.class), OPERATION_EXECUTOR); + mongoDriverInformation, mock, OPERATION_EXECUTOR); } } 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 00667507e17..669b4100e83 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -38,6 +38,7 @@ import com.mongodb.connection.ClusterDescription; import com.mongodb.connection.SocketSettings; import com.mongodb.internal.TimeoutSettings; +import com.mongodb.internal.VisibleForTesting; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.connection.DefaultClusterFactory; import com.mongodb.internal.connection.InternalConnectionPoolSettings; @@ -81,7 +82,8 @@ public MongoClientImpl(final Cluster cluster, this(cluster, mongoDriverInformation, settings, externalResourceCloser, null); } - private MongoClientImpl(final Cluster cluster, + @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE) + public MongoClientImpl(final Cluster cluster, final MongoDriverInformation mongoDriverInformation, final MongoClientSettings settings, @Nullable final AutoCloseable externalResourceCloser, diff --git a/driver-sync/src/test/functional/com/mongodb/client/MongoClientTest.java b/driver-sync/src/test/functional/com/mongodb/client/MongoClientTest.java index fb8db8c2ceb..6d3413f032a 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/MongoClientTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/MongoClientTest.java @@ -23,6 +23,7 @@ import com.mongodb.connection.ClusterId; import com.mongodb.event.ClusterListener; import com.mongodb.event.ClusterOpeningEvent; +import com.mongodb.internal.connection.ClientMetadata; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.mockito.MongoMockito; import org.junit.jupiter.api.Test; @@ -36,6 +37,7 @@ import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; class MongoClientTest { @@ -64,10 +66,13 @@ public void clusterOpening(final ClusterOpeningEvent event) { void shouldCloseExternalResources() throws Exception { //given + MongoDriverInformation mongoDriverInformation = MongoDriverInformation.builder().build(); Cluster cluster = MongoMockito.mock( Cluster.class, mockedCluster -> { doNothing().when(mockedCluster).close(); + when(mockedCluster.getClientMetadata()) + .thenReturn(new ClientMetadata("test", mongoDriverInformation)); }); AutoCloseable externalResource = MongoMockito.mock( AutoCloseable.class, @@ -82,7 +87,7 @@ void shouldCloseExternalResources() throws Exception { MongoClientImpl mongoClient = new MongoClientImpl( cluster, MongoClientSettings.builder().build(), - MongoDriverInformation.builder().build(), + mongoDriverInformation, externalResource); //when From 850ebe779eff7ea6125b84d251a773fd3f88bdf9 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 26 Jun 2025 00:09:31 -0700 Subject: [PATCH 36/38] Add metadata logging. JAVA-5870 --- .../mongodb/internal/connection/ClientMetadataTest.java | 9 +++++---- .../reactivestreams/client/internal/MongoClientImpl.java | 5 ++++- .../com/mongodb/client/internal/MongoClientImpl.java | 5 ++++- .../com/mongodb/client/MongoClientSpecification.groovy | 6 +++++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java index 60489736874..bb2e5dc7351 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/ClientMetadataTest.java @@ -58,6 +58,7 @@ */ public class ClientMetadataTest { private static final String APP_NAME = "app name"; + private static final MongoDriverInformation EMPTY_MONGO_DRIVER_INFORMATION = MongoDriverInformation.builder().build(); @Test public void test01ValidAws() { @@ -298,14 +299,14 @@ public void testLimitForOsName() { @Test public void testApplicationNameUnderLimit() { String applicationName = repeat(126, "a") + "\u00A0"; - BsonDocument client = new ClientMetadata(applicationName, null).getBsonDocument(); + BsonDocument client = new ClientMetadata(applicationName, EMPTY_MONGO_DRIVER_INFORMATION).getBsonDocument(); assertEquals(applicationName, client.getDocument("application").getString("name").getValue()); } @Test public void testApplicationNameOverLimit() { String applicationName = repeat(127, "a") + "\u00A0"; - assertThrows(IllegalArgumentException.class, () -> new ClientMetadata(applicationName, null)); + assertThrows(IllegalArgumentException.class, () -> new ClientMetadata(applicationName, EMPTY_MONGO_DRIVER_INFORMATION)); } @ParameterizedTest @@ -316,7 +317,7 @@ public void testApplicationNameOverLimit() { ", " + false, }) public void testCreateClientMetadataDocument(@Nullable final String appName, final boolean hasDriverInfo) { - MongoDriverInformation driverInformation = hasDriverInfo ? createDriverInformation() : null; + MongoDriverInformation driverInformation = hasDriverInfo ? createDriverInformation() : EMPTY_MONGO_DRIVER_INFORMATION; ClientMetadata clientMetadata = new ClientMetadata(appName, driverInformation); assertEquals( createExpectedClientMetadataDocument(appName, driverInformation), @@ -402,7 +403,7 @@ private void performHello() { } private BsonDocument createActualClientMetadataDocument() { - return new ClientMetadata(APP_NAME, null).getBsonDocument(); + return new ClientMetadata(APP_NAME, EMPTY_MONGO_DRIVER_INFORMATION).getBsonDocument(); } private static MongoDriverInformation createDriverInformation() { 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 18c28418617..07a17badcd7 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 @@ -29,6 +29,7 @@ import com.mongodb.client.model.bulk.ClientNamespacedWriteModel; import com.mongodb.connection.ClusterDescription; import com.mongodb.internal.TimeoutSettings; +import com.mongodb.internal.connection.ClientMetadata; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.diagnostics.logging.Logger; import com.mongodb.internal.diagnostics.logging.Loggers; @@ -328,6 +329,8 @@ public ClusterDescription getClusterDescription() { @Override public void appendMetadata(final MongoDriverInformation mongoDriverInformation) { - getCluster().getClientMetadata().append(mongoDriverInformation); + ClientMetadata clientMetadata = getCluster().getClientMetadata(); + clientMetadata.append(mongoDriverInformation); + LOGGER.info(format("MongoClient metadata has been updated to %s", clientMetadata.getBsonDocument())); } } 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 669b4100e83..6870277b1c6 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -39,6 +39,7 @@ import com.mongodb.connection.SocketSettings; import com.mongodb.internal.TimeoutSettings; import com.mongodb.internal.VisibleForTesting; +import com.mongodb.internal.connection.ClientMetadata; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.connection.DefaultClusterFactory; import com.mongodb.internal.connection.InternalConnectionPoolSettings; @@ -138,7 +139,9 @@ public ClusterDescription getClusterDescription() { @Override public void appendMetadata(final MongoDriverInformation mongoDriverInformation) { - delegate.getCluster().getClientMetadata().append(mongoDriverInformation); + ClientMetadata clientMetadata = getCluster().getClientMetadata(); + clientMetadata.append(mongoDriverInformation); + LOGGER.info(format("MongoClient metadata has been updated to %s", clientMetadata.getBsonDocument())); } @Override diff --git a/driver-sync/src/test/unit/com/mongodb/client/MongoClientSpecification.groovy b/driver-sync/src/test/unit/com/mongodb/client/MongoClientSpecification.groovy index 95004ddedf8..ade491b6a6b 100644 --- a/driver-sync/src/test/unit/com/mongodb/client/MongoClientSpecification.groovy +++ b/driver-sync/src/test/unit/com/mongodb/client/MongoClientSpecification.groovy @@ -17,6 +17,7 @@ package com.mongodb.client import com.mongodb.MongoClientSettings +import com.mongodb.MongoDriverInformation import com.mongodb.MongoNamespace import com.mongodb.ReadConcern import com.mongodb.ServerAddress @@ -34,6 +35,7 @@ import com.mongodb.connection.ServerDescription import com.mongodb.connection.ServerType import com.mongodb.internal.TimeoutSettings import com.mongodb.internal.client.model.changestream.ChangeStreamLevel +import com.mongodb.internal.connection.ClientMetadata import com.mongodb.internal.connection.Cluster import org.bson.BsonDocument import org.bson.Document @@ -191,13 +193,15 @@ class MongoClientSpecification extends Specification { .type(ServerType.UNKNOWN) .state(ServerConnectionState.CONNECTING) .build()]) + def driverInformation = MongoDriverInformation.builder().build() def cluster = Mock(Cluster) { 1 * getCurrentDescription() >> { clusterDescription } + 1 * getClientMetadata() >> new ClientMetadata("test", driverInformation) } def settings = MongoClientSettings.builder().build() - def client = new MongoClientImpl(cluster, null, settings, null, new TestOperationExecutor([])) + def client = new MongoClientImpl(cluster, driverInformation, settings, null, new TestOperationExecutor([])) expect: client.getClusterDescription() == clusterDescription From cb40b6acb2d7acd7d68c2cb4eaacf655105196d3 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 30 Jun 2025 12:14:36 -0700 Subject: [PATCH 37/38] Add test assertion. --- .../AbstractClientMetadataProseTest.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 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 28465633016..5bc0ff5936d 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -25,6 +25,7 @@ import com.mongodb.lang.Nullable; import org.bson.BsonDocument; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -96,8 +97,8 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization(@Nullable fina builder.maxConnectionIdleTime(1, TimeUnit.MILLISECONDS)) .build())) { - //TODO change get() to orElseThrow - BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient) + .orElseThrow(AbstractClientMetadataProseTest::failOnEmptyMetadata); BsonDocument driverInformation = initialClientMetadata.getDocument("driver"); String generatedDriverName = driverInformation.get("name").asString().getValue(); String generatedVersionName = driverInformation.get("version").asString().getValue(); @@ -108,8 +109,8 @@ void shouldAppendToPreviousMetadataWhenUpdatedAfterInitialization(@Nullable fina updateClientMetadata(driverVersion, driverName, driverPlatform, mongoClient); //then - //TODO change get() to orElseThrow - BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient) + .orElseThrow(AbstractClientMetadataProseTest::failOnEmptyMetadata); BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); String expectedDriverName = driverName == null ? generatedDriverName : generatedDriverName + "|" + driverName; @@ -137,8 +138,8 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable builder.maxConnectionIdleTime(1, TimeUnit.MILLISECONDS)) .build())) { - //TODO change get() to orElseThrow - BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument initialClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient) + .orElseThrow(AbstractClientMetadataProseTest::failOnEmptyMetadata); BsonDocument generatedDriverInformation = initialClientMetadata.getDocument("driver"); String generatedDriverName = generatedDriverInformation.get("name").asString().getValue(); @@ -150,8 +151,8 @@ void shouldAppendToDefaultClientMetadataWhenUpdatedAfterInitialization(@Nullable updateClientMetadata(driverVersion, driverName, driverPlatform, mongoClient); //then - //TODO change get() to orElseThrow - BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument updatedClientMetadata = executePingAndCaptureMetadataHandshake(mongoClient) + .orElseThrow(AbstractClientMetadataProseTest::failOnEmptyMetadata); BsonDocument updatedDriverInformation = updatedClientMetadata.getDocument("driver"); String expectedDriverName = driverName == null ? generatedDriverName : generatedDriverName + "|" + driverName; @@ -182,8 +183,8 @@ void shouldAppendProvidedMetadatDuringInitialization() { .build())) { //when - //TODO change get() to orElseThrow - BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient).get(); + BsonDocument clientMetadata = executePingAndCaptureMetadataHandshake(mongoClient) + .orElseThrow(AbstractClientMetadataProseTest::failOnEmptyMetadata); BsonDocument driverInformation = clientMetadata.getDocument("driver"); //then @@ -235,4 +236,8 @@ private static void updateClientMetadata(@Nullable final String driverVersion, ofNullable(driverPlatform).ifPresent(builder::driverPlatform); mongoClient.appendMetadata(builder.build()); } + + private static AssertionError failOnEmptyMetadata() { + return Assertions.fail("Client metadata was expected to be present after ping command"); + } } From c44200fae93d68f70694f4b12ff964c47454df32 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 30 Jun 2025 15:09:30 -0700 Subject: [PATCH 38/38] Fix GSSAPI test. --- .../connection/GSSAPIAuthenticatorSpecification.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/driver-core/src/test/functional/com/mongodb/internal/connection/GSSAPIAuthenticatorSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/connection/GSSAPIAuthenticatorSpecification.groovy index f18a6915e38..223698d561c 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/connection/GSSAPIAuthenticatorSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/connection/GSSAPIAuthenticatorSpecification.groovy @@ -19,6 +19,7 @@ package com.mongodb.internal.connection import com.mongodb.ClusterFixture import com.mongodb.LoggerSettings import com.mongodb.MongoCompressor +import com.mongodb.MongoDriverInformation import com.mongodb.SubjectProvider import com.mongodb.connection.ClusterId import com.mongodb.connection.ServerId @@ -49,8 +50,10 @@ class GSSAPIAuthenticatorSpecification extends Specification { def credential = ClusterFixture.getCredential().withMechanismProperty(JAVA_SUBJECT_PROVIDER_KEY, subjectProvider) def credentialWithCache = new MongoCredentialWithCache(credential) def streamFactory = new SocketStreamFactory(new DefaultInetAddressResolver(), SocketSettings.builder().build(), getSslSettings()) - def internalConnection = new InternalStreamConnectionFactory(SINGLE, streamFactory, credentialWithCache, null, - null, Collections. emptyList(), LoggerSettings.builder().build(), null, getServerApi()) + def internalConnection = new InternalStreamConnectionFactory( + SINGLE, streamFactory, + credentialWithCache, new ClientMetadata("test", MongoDriverInformation.builder().build()), + Collections. emptyList(), LoggerSettings.builder().build(), null, getServerApi()) .create(new ServerId(new ClusterId(), getPrimary())) when: