diff --git a/driver-core/src/main/com/mongodb/internal/InternalMongoClientSettings.java b/driver-core/src/main/com/mongodb/internal/InternalMongoClientSettings.java new file mode 100644 index 00000000000..8e4bb9440c6 --- /dev/null +++ b/driver-core/src/main/com/mongodb/internal/InternalMongoClientSettings.java @@ -0,0 +1,159 @@ +/* + * 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; + +import com.mongodb.annotations.Immutable; +import com.mongodb.internal.connection.InternalConnectionPoolSettings; + +import java.util.Objects; + +/** + * Internal settings for MongoClient that are not part of the public API. + * Used for testing and internal configuration purposes. + * + *
This class is not part of the public API and may be removed or changed at any time
+ */ +@Immutable +public final class InternalMongoClientSettings { + + private static final InternalMongoClientSettings DEFAULTS = builder().build(); + + private final InternalConnectionPoolSettings internalConnectionPoolSettings; + private final boolean recordEverything; + + private InternalMongoClientSettings(final Builder builder) { + this.internalConnectionPoolSettings = builder.internalConnectionPoolSettings != null + ? builder.internalConnectionPoolSettings + : InternalConnectionPoolSettings.builder().build(); + this.recordEverything = builder.recordEverything; + } + + /** + * Gets the default internal settings for production use. + * + * @return the default settings + */ + public static InternalMongoClientSettings getDefaults() { + return DEFAULTS; + } + + /** + * Creates a new builder for InternalMongoClientSettings. + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Gets the internal connection pool settings. + * + * @return the internal connection pool settings + */ + public InternalConnectionPoolSettings getInternalConnectionPoolSettings() { + return internalConnectionPoolSettings; + } + + /** + * Indicates whether to record all commands including security-sensitive commands + * (like authentication commands) to the command listener. + *+ * When disabled (the default for production), security-sensitive commands are not recorded + * to the command listener for security reasons. When enabled, all commands are recorded, + * which is useful for testing authentication and handshake behavior. + *
+ * + * @return true if all commands should be recorded + */ + public boolean isRecordEverything() { + return recordEverything; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalMongoClientSettings that = (InternalMongoClientSettings) o; + return recordEverything == that.recordEverything + && Objects.equals(internalConnectionPoolSettings, that.internalConnectionPoolSettings); + } + + @Override + public int hashCode() { + return Objects.hash(internalConnectionPoolSettings, recordEverything); + } + + @Override + public String toString() { + return "InternalMongoClientSettings{" + + "internalConnectionPoolSettings=" + internalConnectionPoolSettings + + ", recordEverything=" + recordEverything + + '}'; + } + + /** + * A builder for InternalMongoClientSettings. + */ + public static final class Builder { + private InternalConnectionPoolSettings internalConnectionPoolSettings = InternalConnectionPoolSettings.builder().build(); + private boolean recordEverything = false; + + private Builder() { + } + + /** + * Sets the internal connection pool settings. + * + * @param internalConnectionPoolSettings the internal connection pool settings + * @return this + */ + public Builder internalConnectionPoolSettings( + final InternalConnectionPoolSettings internalConnectionPoolSettings) { + this.internalConnectionPoolSettings = internalConnectionPoolSettings; + return this; + } + + /** + * Sets whether to record all commands including security-sensitive commands. + *+ * Default is {@code false} for production use. Set to {@code true} for testing + * authentication and handshake behavior. + *
+ * + * @param recordEverything whether to record all commands + * @return this + */ + public Builder recordEverything(final boolean recordEverything) { + this.recordEverything = recordEverything; + return this; + } + + /** + * Builds the InternalMongoClientSettings. + * + * @return the internal settings + */ + public InternalMongoClientSettings build() { + return new InternalMongoClientSettings(this); + } + } +} + 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 ac853cb002e..9940d8196c5 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java @@ -31,6 +31,7 @@ import com.mongodb.event.CommandListener; import com.mongodb.event.ServerListener; import com.mongodb.event.ServerMonitorListener; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.internal.TimeoutSettings; import com.mongodb.internal.VisibleForTesting; import com.mongodb.internal.diagnostics.logging.Logger; @@ -60,7 +61,7 @@ public final class DefaultClusterFactory { public Cluster createCluster(final ClusterSettings originalClusterSettings, final ServerSettings originalServerSettings, final ConnectionPoolSettings connectionPoolSettings, - final InternalConnectionPoolSettings internalConnectionPoolSettings, + final InternalMongoClientSettings internalSettings, final TimeoutSettings clusterTimeoutSettings, final StreamFactory streamFactory, final TimeoutSettings heartbeatTimeoutSettings, @@ -113,12 +114,12 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina if (clusterSettings.getMode() == ClusterConnectionMode.LOAD_BALANCED) { ClusterableServerFactory serverFactory = new LoadBalancedClusterableServerFactory(serverSettings, - connectionPoolSettings, internalConnectionPoolSettings, streamFactory, credential, loggerSettings, commandListener, + connectionPoolSettings, internalSettings, streamFactory, credential, loggerSettings, commandListener, compressorList, serverApi, clusterOperationContextFactory); return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, clientMetadata, dnsSrvRecordMonitorFactory); } else { ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(serverSettings, - connectionPoolSettings, internalConnectionPoolSettings, + connectionPoolSettings, internalSettings, clusterOperationContextFactory, streamFactory, heartBeatOperationContextFactory, heartbeatStreamFactory, credential, loggerSettings, commandListener, compressorList, serverApi, FaasEnvironment.getFaasEnvironment() != FaasEnvironment.UNKNOWN); 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 cb9830c4017..90833a0f585 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java @@ -27,6 +27,7 @@ import com.mongodb.connection.ServerSettings; import com.mongodb.event.CommandListener; import com.mongodb.event.ServerListener; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.internal.inject.SameObjectProvider; import com.mongodb.lang.Nullable; @@ -41,7 +42,7 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory { private final ServerSettings serverSettings; private final ConnectionPoolSettings connectionPoolSettings; - private final InternalConnectionPoolSettings internalConnectionPoolSettings; + private final InternalMongoClientSettings internalSettings; private final InternalOperationContextFactory clusterOperationContextFactory; private final StreamFactory streamFactory; private final InternalOperationContextFactory heartbeatOperationContextFactory; @@ -56,7 +57,7 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory public DefaultClusterableServerFactory( final ServerSettings serverSettings, final ConnectionPoolSettings connectionPoolSettings, - final InternalConnectionPoolSettings internalConnectionPoolSettings, + final InternalMongoClientSettings internalSettings, final InternalOperationContextFactory clusterOperationContextFactory, final StreamFactory streamFactory, final InternalOperationContextFactory heartbeatOperationContextFactory, final StreamFactory heartbeatStreamFactory, @Nullable final MongoCredential credential, final LoggerSettings loggerSettings, @@ -64,7 +65,7 @@ public DefaultClusterableServerFactory( final ListThis class is not part of the public API and may be removed or changed at any time
+ */ +public final class InternalMongoClients { + + private InternalMongoClients() { + } + + /** + * Creates a new client with the default connection string "mongodb://localhost" and the given internal settings. + * + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final InternalMongoClientSettings internalSettings) { + return create(new ConnectionString("mongodb://localhost"), internalSettings); + } + + /** + * Creates a new client with the given connection string and internal settings. + * + * @param connectionString the connection string + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final String connectionString, + final InternalMongoClientSettings internalSettings) { + return create(new ConnectionString(connectionString), internalSettings); + } + + /** + * Creates a new client with the given connection string and internal settings. + * + * @param connectionString the connection string + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final ConnectionString connectionString, + final InternalMongoClientSettings internalSettings) { + return create(connectionString, null, internalSettings); + } + + /** + * Creates a new client with the given connection string, driver information and internal settings. + * + * @param connectionString the connection string + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final ConnectionString connectionString, + @Nullable final MongoDriverInformation mongoDriverInformation, + final InternalMongoClientSettings internalSettings) { + return create(MongoClientSettings.builder().applyConnectionString(connectionString).build(), + mongoDriverInformation, internalSettings); + } + + /** + * Creates a new client with the given client settings and internal settings. + * + * @param settings the public settings + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final MongoClientSettings settings, + final InternalMongoClientSettings internalSettings) { + return create(settings, null, internalSettings); + } + + /** + * Creates a new client with the given client settings, driver information and internal settings. + * + * @param settings the public settings + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final MongoClientSettings settings, + @Nullable final MongoDriverInformation mongoDriverInformation, + final InternalMongoClientSettings internalSettings) { + notNull("settings", settings); + notNull("internalSettings", internalSettings); + + if (settings.getSocketSettings().getProxySettings().isProxyEnabled()) { + throw new MongoClientException("Proxy is not supported for reactive clients"); + } + InetAddressResolver inetAddressResolver = getInetAddressResolver(settings); + StreamFactoryFactory streamFactoryFactory = getAsyncStreamFactoryFactory(settings, inetAddressResolver); + StreamFactory streamFactory = getStreamFactory(streamFactoryFactory, settings, false); + StreamFactory heartbeatStreamFactory = getStreamFactory(streamFactoryFactory, settings, true); + MongoDriverInformation wrappedMongoDriverInformation = wrapMongoDriverInformation(mongoDriverInformation); + Cluster cluster = createCluster(settings, wrappedMongoDriverInformation, streamFactory, heartbeatStreamFactory, internalSettings); + return new MongoClientImpl(settings, wrappedMongoDriverInformation, cluster, streamFactoryFactory); + } + + private static Cluster createCluster(final MongoClientSettings settings, + @Nullable final MongoDriverInformation mongoDriverInformation, + final StreamFactory streamFactory, + final StreamFactory heartbeatStreamFactory, + final InternalMongoClientSettings internalSettings) { + notNull("settings", settings); + return new DefaultClusterFactory().createCluster(settings.getClusterSettings(), settings.getServerSettings(), + settings.getConnectionPoolSettings(), internalSettings, + TimeoutSettings.create(settings), streamFactory, TimeoutSettings.createHeartbeatSettings(settings), heartbeatStreamFactory, + settings.getCredential(), settings.getLoggerSettings(), getCommandListener(settings.getCommandListeners()), + settings.getApplicationName(), mongoDriverInformation, settings.getCompressorList(), settings.getServerApi(), + settings.getDnsClient()); + } + + private static MongoDriverInformation wrapMongoDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) { + return (mongoDriverInformation == null ? MongoDriverInformation.builder() : MongoDriverInformation.builder(mongoDriverInformation)) + .driverName("reactive-streams").build(); + } + + private static StreamFactory getStreamFactory( + final StreamFactoryFactory streamFactoryFactory, final MongoClientSettings settings, + final boolean isHeartbeat) { + SocketSettings socketSettings = isHeartbeat + ? settings.getHeartbeatSocketSettings() : settings.getSocketSettings(); + return streamFactoryFactory.create(socketSettings, settings.getSslSettings()); + } +} + 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 index 8aa4db32e79..1ebe60d1769 100644 --- 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 @@ -20,7 +20,9 @@ import com.mongodb.MongoDriverInformation; import com.mongodb.client.AbstractClientMetadataProseTest; import com.mongodb.client.MongoClient; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.lang.Nullable; +import com.mongodb.reactivestreams.client.internal.InternalMongoClients; import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient; /** @@ -29,6 +31,7 @@ class ClientMetadataProseTest extends AbstractClientMetadataProseTest { protected MongoClient createMongoClient(@Nullable final MongoDriverInformation mongoDriverInformation, final MongoClientSettings mongoClientSettings) { - return new SyncMongoClient(mongoClientSettings, mongoDriverInformation); + return new SyncMongoClient(InternalMongoClients.create(mongoClientSettings, mongoDriverInformation, + InternalMongoClientSettings.builder().recordEverything(true).build())); } } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java index b922ec20b71..cd478d60d97 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java @@ -28,8 +28,10 @@ import com.mongodb.client.model.changestream.FullDocument; import com.mongodb.event.CommandFailedEvent; import com.mongodb.event.CommandStartedEvent; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.reactivestreams.client.gridfs.GridFSBucket; import com.mongodb.reactivestreams.client.gridfs.GridFSBuckets; +import com.mongodb.reactivestreams.client.internal.InternalMongoClients; import com.mongodb.reactivestreams.client.syncadapter.SyncGridFSBucket; import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient; import org.bson.BsonDocument; @@ -80,6 +82,15 @@ protected com.mongodb.client.MongoClient createMongoClient(final MongoClientSett return client; } + @Override + protected com.mongodb.client.MongoClient createMongoClientWithInternalSettings(final MongoClientSettings mongoClientSettings, + final InternalMongoClientSettings internalSettings) { + MongoClient reactiveClient = InternalMongoClients.create(mongoClientSettings, null, internalSettings); + SyncMongoClient client = new SyncMongoClient(reactiveClient); + wrapped = reactiveClient; + return client; + } + private static MongoClient createReactiveClient(final MongoClientSettings.Builder builder) { return MongoClients.create(builder.build()); } 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 348bef110ff..59e912edc7f 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 @@ -158,6 +158,17 @@ public SyncMongoClient(final MongoClientSettings.Builder builder, @Nullable fina this.delegate = new SyncMongoCluster(wrapped); } + /** + * Wraps an existing reactive MongoClient as a sync client adapter. + * + * @param reactiveMongoClient the reactive MongoClient to wrap + */ + public SyncMongoClient(final com.mongodb.reactivestreams.client.MongoClient reactiveMongoClient) { + this.connectionPoolCounter = new ConnectionPoolCounter(); + this.wrapped = reactiveMongoClient; + this.delegate = new SyncMongoCluster(wrapped); + } + public com.mongodb.reactivestreams.client.MongoClient getWrapped() { return wrapped; } diff --git a/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/InternalMongoClientsTest.java b/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/InternalMongoClientsTest.java new file mode 100644 index 00000000000..4db711ae3b5 --- /dev/null +++ b/driver-reactive-streams/src/test/unit/com/mongodb/reactivestreams/client/internal/InternalMongoClientsTest.java @@ -0,0 +1,53 @@ +/* + * 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.internal; + +import com.mongodb.MongoClientSettings; +import com.mongodb.internal.InternalMongoClientSettings; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Unit tests for reactive {@link InternalMongoClients}. + * See sync InternalMongoClientsTest for comprehensive InternalMongoClientSettings tests. + */ +class InternalMongoClientsTest { + + @Test + void testCreateMethodsValidateNullSettings() { + // Verify that null MongoClientSettings is rejected + assertThrows(IllegalArgumentException.class, () -> + InternalMongoClients.create((MongoClientSettings) null, InternalMongoClientSettings.getDefaults())); + + // Verify that null InternalMongoClientSettings is rejected + MongoClientSettings settings = MongoClientSettings.builder().build(); + assertThrows(IllegalArgumentException.class, () -> + InternalMongoClients.create(settings, null)); + } + + @Test + void testInternalSettingsArePreserved() { + // Verify that InternalMongoClientSettings can be built with recordEverything for reactive clients + InternalMongoClientSettings internalSettings = InternalMongoClientSettings.builder() + .recordEverything(true) + .build(); + assertNotNull(internalSettings); + } +} + diff --git a/driver-sync/src/main/com/mongodb/client/MongoClients.java b/driver-sync/src/main/com/mongodb/client/MongoClients.java index e0e59ba5f78..51dd58868d5 100644 --- a/driver-sync/src/main/com/mongodb/client/MongoClients.java +++ b/driver-sync/src/main/com/mongodb/client/MongoClients.java @@ -19,16 +19,10 @@ import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; import com.mongodb.MongoDriverInformation; -import com.mongodb.client.internal.Clusters; -import com.mongodb.client.internal.MongoClientImpl; -import com.mongodb.internal.connection.Cluster; -import com.mongodb.internal.connection.StreamFactoryFactory; +import com.mongodb.client.internal.InternalMongoClients; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.lang.Nullable; -import static com.mongodb.assertions.Assertions.notNull; -import static com.mongodb.internal.connection.ServerAddressHelper.getInetAddressResolver; -import static com.mongodb.internal.connection.StreamFactoryHelper.getSyncStreamFactoryFactory; - /** * A factory for {@link MongoClient} instances. Use of this class is now the recommended way to connect to MongoDB via the Java driver. @@ -38,13 +32,15 @@ */ public final class MongoClients { + private static final InternalMongoClientSettings DEFAULT_INTERNAL_SETTINGS = InternalMongoClientSettings.getDefaults(); + /** * Creates a new client with the default connection string "mongodb://localhost". * * @return the client */ public static MongoClient create() { - return create(new ConnectionString("mongodb://localhost")); + return InternalMongoClients.create(DEFAULT_INTERNAL_SETTINGS); } /** @@ -54,7 +50,7 @@ public static MongoClient create() { * @return the client */ public static MongoClient create(final MongoClientSettings settings) { - return create(settings, null); + return InternalMongoClients.create(settings, DEFAULT_INTERNAL_SETTINGS); } /** @@ -65,7 +61,7 @@ public static MongoClient create(final MongoClientSettings settings) { * @see #create(ConnectionString) */ public static MongoClient create(final String connectionString) { - return create(new ConnectionString(connectionString)); + return InternalMongoClients.create(connectionString, DEFAULT_INTERNAL_SETTINGS); } /** @@ -82,7 +78,7 @@ public static MongoClient create(final String connectionString) { * @see com.mongodb.MongoClientSettings.Builder#applyConnectionString(ConnectionString) */ public static MongoClient create(final ConnectionString connectionString) { - return create(connectionString, null); + return InternalMongoClients.create(connectionString, DEFAULT_INTERNAL_SETTINGS); } /** @@ -97,7 +93,7 @@ public static MongoClient create(final ConnectionString connectionString) { */ public static MongoClient create(final ConnectionString connectionString, @Nullable final MongoDriverInformation mongoDriverInformation) { - return create(MongoClientSettings.builder().applyConnectionString(connectionString).build(), mongoDriverInformation); + return InternalMongoClients.create(connectionString, mongoDriverInformation, DEFAULT_INTERNAL_SETTINGS); } /** @@ -110,23 +106,7 @@ public static MongoClient create(final ConnectionString connectionString, * @return the client */ public static MongoClient create(final MongoClientSettings settings, @Nullable final MongoDriverInformation mongoDriverInformation) { - notNull("settings", settings); - - MongoDriverInformation.Builder builder = mongoDriverInformation == null ? MongoDriverInformation.builder() - : MongoDriverInformation.builder(mongoDriverInformation); - - MongoDriverInformation driverInfo = builder.driverName("sync").build(); - - StreamFactoryFactory syncStreamFactoryFactory = getSyncStreamFactoryFactory( - settings.getTransportSettings(), - getInetAddressResolver(settings)); - - Cluster cluster = Clusters.createCluster( - settings, - driverInfo, - syncStreamFactoryFactory); - - return new MongoClientImpl(cluster, settings, driverInfo, syncStreamFactoryFactory); + return InternalMongoClients.create(settings, mongoDriverInformation, DEFAULT_INTERNAL_SETTINGS); } private MongoClients() { diff --git a/driver-sync/src/main/com/mongodb/client/internal/Clusters.java b/driver-sync/src/main/com/mongodb/client/internal/Clusters.java index 6c57505e090..7e09ee7d909 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/Clusters.java +++ b/driver-sync/src/main/com/mongodb/client/internal/Clusters.java @@ -21,7 +21,7 @@ import com.mongodb.internal.TimeoutSettings; import com.mongodb.internal.connection.Cluster; import com.mongodb.internal.connection.DefaultClusterFactory; -import com.mongodb.internal.connection.InternalConnectionPoolSettings; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.internal.connection.StreamFactory; import com.mongodb.internal.connection.StreamFactoryFactory; import com.mongodb.lang.Nullable; @@ -35,17 +35,42 @@ private Clusters() { //NOP } + /** + * Creates a cluster with the given settings and default internal settings. + * + * @param settings the client settings + * @param mongoDriverInformation driver information + * @param streamFactoryFactory the stream factory + * @return the cluster + */ public static Cluster createCluster(final MongoClientSettings settings, @Nullable final MongoDriverInformation mongoDriverInformation, final StreamFactoryFactory streamFactoryFactory) { + return createCluster(settings, mongoDriverInformation, streamFactoryFactory, InternalMongoClientSettings.getDefaults()); + } + + /** + * Creates a cluster with the given settings and internal settings. + * + * @param settings the client settings + * @param mongoDriverInformation driver information + * @param streamFactoryFactory the stream factory + * @param internalSettings the internal settings + * @return the cluster + */ + public static Cluster createCluster(final MongoClientSettings settings, + @Nullable final MongoDriverInformation mongoDriverInformation, + final StreamFactoryFactory streamFactoryFactory, + final InternalMongoClientSettings internalSettings) { assertNotNull(streamFactoryFactory); assertNotNull(settings); + assertNotNull(internalSettings); StreamFactory streamFactory = getStreamFactory(streamFactoryFactory, settings, false); StreamFactory heartbeatStreamFactory = getStreamFactory(streamFactoryFactory, settings, true); return new DefaultClusterFactory().createCluster(settings.getClusterSettings(), settings.getServerSettings(), - settings.getConnectionPoolSettings(), InternalConnectionPoolSettings.builder().build(), + settings.getConnectionPoolSettings(), internalSettings, TimeoutSettings.create(settings), streamFactory, TimeoutSettings.createHeartbeatSettings(settings), heartbeatStreamFactory, settings.getCredential(), settings.getLoggerSettings(), getCommandListener(settings.getCommandListeners()), diff --git a/driver-sync/src/main/com/mongodb/client/internal/InternalMongoClients.java b/driver-sync/src/main/com/mongodb/client/internal/InternalMongoClients.java new file mode 100644 index 00000000000..6d6edceb160 --- /dev/null +++ b/driver-sync/src/main/com/mongodb/client/internal/InternalMongoClients.java @@ -0,0 +1,135 @@ +/* + * 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.internal; + +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoDriverInformation; +import com.mongodb.client.MongoClient; +import com.mongodb.internal.connection.Cluster; +import com.mongodb.internal.InternalMongoClientSettings; +import com.mongodb.internal.connection.StreamFactoryFactory; +import com.mongodb.lang.Nullable; + +import static com.mongodb.assertions.Assertions.notNull; +import static com.mongodb.internal.connection.ServerAddressHelper.getInetAddressResolver; +import static com.mongodb.internal.connection.StreamFactoryHelper.getSyncStreamFactoryFactory; + +/** + * Internal factory for MongoClient instances. + * + *This class is not part of the public API and may be removed or changed at any time
+ */ +public final class InternalMongoClients { + + private InternalMongoClients() { + } + + /** + * Creates a new client with the default connection string "mongodb://localhost" and the given internal settings. + * + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final InternalMongoClientSettings internalSettings) { + return create(new ConnectionString("mongodb://localhost"), internalSettings); + } + + /** + * Creates a new client with the given connection string and internal settings. + * + * @param connectionString the connection string + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final String connectionString, + final InternalMongoClientSettings internalSettings) { + return create(new ConnectionString(connectionString), internalSettings); + } + + /** + * Creates a new client with the given connection string and internal settings. + * + * @param connectionString the connection string + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final ConnectionString connectionString, + final InternalMongoClientSettings internalSettings) { + return create(connectionString, null, internalSettings); + } + + /** + * Creates a new client with the given connection string, driver information and internal settings. + * + * @param connectionString the connection string + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final ConnectionString connectionString, + @Nullable final MongoDriverInformation mongoDriverInformation, + final InternalMongoClientSettings internalSettings) { + return create(MongoClientSettings.builder().applyConnectionString(connectionString).build(), + mongoDriverInformation, internalSettings); + } + + /** + * Creates a new client with the given client settings and internal settings. + * + * @param settings the public settings + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final MongoClientSettings settings, + final InternalMongoClientSettings internalSettings) { + return create(settings, null, internalSettings); + } + + /** + * Creates a new client with the given client settings, driver information and internal settings. + * + * @param settings the public settings + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @param internalSettings the internal settings + * @return the client + */ + public static MongoClient create(final MongoClientSettings settings, + @Nullable final MongoDriverInformation mongoDriverInformation, + final InternalMongoClientSettings internalSettings) { + notNull("settings", settings); + notNull("internalSettings", internalSettings); + + MongoDriverInformation.Builder builder = mongoDriverInformation == null ? MongoDriverInformation.builder() + : MongoDriverInformation.builder(mongoDriverInformation); + + MongoDriverInformation driverInfo = builder.driverName("sync").build(); + + StreamFactoryFactory syncStreamFactoryFactory = getSyncStreamFactoryFactory( + settings.getTransportSettings(), + getInetAddressResolver(settings)); + + Cluster cluster = Clusters.createCluster( + settings, + driverInfo, + syncStreamFactoryFactory, + internalSettings); + + return new MongoClientImpl(cluster, settings, driverInfo, syncStreamFactoryFactory); + } +} + 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 bbeb7419bc7..42d6bd93ce0 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java +++ b/driver-sync/src/main/com/mongodb/client/internal/MongoClientImpl.java @@ -36,15 +36,10 @@ import com.mongodb.client.model.bulk.ClientBulkWriteResult; import com.mongodb.client.model.bulk.ClientNamespacedWriteModel; 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.ClientMetadata; import com.mongodb.internal.connection.Cluster; -import com.mongodb.internal.connection.DefaultClusterFactory; -import com.mongodb.internal.connection.InternalConnectionPoolSettings; -import com.mongodb.internal.connection.StreamFactory; -import com.mongodb.internal.connection.StreamFactoryFactory; import com.mongodb.internal.diagnostics.logging.Logger; import com.mongodb.internal.diagnostics.logging.Loggers; import com.mongodb.internal.session.ServerSessionPool; @@ -61,7 +56,6 @@ import static com.mongodb.assertions.Assertions.notNull; import static com.mongodb.client.internal.Crypts.createCrypt; -import static com.mongodb.internal.event.EventListenerHelper.getCommandListener; import static java.lang.String.format; import static org.bson.codecs.configuration.CodecRegistries.withUuidRepresentation; @@ -310,27 +304,6 @@ public ClientBulkWriteResult bulkWrite( return delegate.bulkWrite(clientSession, clientWriteModels, options); } - private static Cluster createCluster(final MongoClientSettings settings, - @Nullable final MongoDriverInformation mongoDriverInformation, - final StreamFactory streamFactory, final StreamFactory heartbeatStreamFactory) { - notNull("settings", settings); - return new DefaultClusterFactory().createCluster(settings.getClusterSettings(), settings.getServerSettings(), - settings.getConnectionPoolSettings(), InternalConnectionPoolSettings.builder().build(), - TimeoutSettings.create(settings), streamFactory, - TimeoutSettings.createHeartbeatSettings(settings), heartbeatStreamFactory, - settings.getCredential(), settings.getLoggerSettings(), getCommandListener(settings.getCommandListeners()), - settings.getApplicationName(), mongoDriverInformation, settings.getCompressorList(), settings.getServerApi(), - settings.getDnsClient()); - } - - private static StreamFactory getStreamFactory( - final StreamFactoryFactory streamFactoryFactory, - final MongoClientSettings settings, - final boolean isHeartbeat) { - SocketSettings socketSettings = isHeartbeat ? settings.getHeartbeatSocketSettings() : settings.getSocketSettings(); - return streamFactoryFactory.create(socketSettings, settings.getSslSettings()); - } - public Cluster getCluster() { return delegate.getCluster(); } 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 b958afcf145..639a2f687f5 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientMetadataProseTest.java @@ -20,12 +20,10 @@ import com.mongodb.MongoDriverInformation; import com.mongodb.event.CommandStartedEvent; import com.mongodb.internal.client.DriverInformation; -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.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -65,12 +63,6 @@ public void setUp() { commandListener = new TestCommandListener(); connectionPoolListener = new TestConnectionPoolListener(); - InternalStreamConnection.setRecordEverything(true); - } - - @AfterEach - public void tearDown() { - InternalStreamConnection.setRecordEverything(false); } @DisplayName("Test 1: Test that the driver updates metadata") diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java index 9ce58b1654f..19178c89d6d 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java @@ -47,11 +47,7 @@ import com.mongodb.event.ConnectionClosedEvent; import com.mongodb.event.ConnectionCreatedEvent; import com.mongodb.event.ConnectionReadyEvent; - -import static com.mongodb.internal.connection.CommandHelper.HELLO; -import static com.mongodb.internal.connection.CommandHelper.LEGACY_HELLO; - -import com.mongodb.internal.connection.InternalStreamConnection; +import com.mongodb.internal.InternalMongoClientSettings; import com.mongodb.internal.connection.ServerHelper; import com.mongodb.internal.connection.TestCommandListener; import com.mongodb.internal.connection.TestConnectionPoolListener; @@ -93,6 +89,8 @@ import static com.mongodb.ClusterFixture.sleep; import static com.mongodb.client.Fixture.getDefaultDatabaseName; import static com.mongodb.client.Fixture.getPrimary; +import static com.mongodb.internal.connection.CommandHelper.HELLO; +import static com.mongodb.internal.connection.CommandHelper.LEGACY_HELLO; import static java.lang.Long.MAX_VALUE; import static java.lang.String.join; import static java.util.Arrays.asList; @@ -134,6 +132,17 @@ public abstract class AbstractClientSideOperationsTimeoutProseTest { protected abstract MongoClient createMongoClient(MongoClientSettings mongoClientSettings); + /** + * Creates a MongoClient with the given settings and internal settings. + * Used for tests that need to configure internal settings like recordEverything. + * + * @param mongoClientSettings the client settings + * @param internalSettings the internal settings + * @return the MongoClient + */ + protected abstract MongoClient createMongoClientWithInternalSettings(MongoClientSettings mongoClientSettings, + InternalMongoClientSettings internalSettings); + protected abstract GridFSBucket createGridFsBucket(MongoDatabase mongoDatabase, String bucketName); protected abstract boolean isAsync(); @@ -1044,16 +1053,15 @@ public void shouldUseConnectTimeoutMsWhenEstablishingConnectionInBackground() { + " }" + "}"); - try (MongoClient ignored = createMongoClient(getMongoClientSettingsBuilder() + try (MongoClient ignored = createMongoClientWithInternalSettings(getMongoClientSettingsBuilder() .applyToConnectionPoolSettings(builder -> builder.minSize(1)) // Use a very short timeout to ensure that the connection establishment will fail on the first handshake command. - .timeout(10, TimeUnit.MILLISECONDS))) { - InternalStreamConnection.setRecordEverything(true); + .timeout(10, TimeUnit.MILLISECONDS) + .build(), + InternalMongoClientSettings.builder().recordEverything(true).build())) { // Wait for the connection to start establishment in the background. sleep(1000); - } finally { - InternalStreamConnection.setRecordEverything(false); } ListThese tests demonstrate how to use InternalMongoClients to create MongoClient instances + * with custom internal settings, avoiding the need for global mutable state in tests.
+ */ +class InternalMongoClientsTest { + + @Test + void testCreateWithDefaultInternalSettings() { + // Tests can now create clients with explicit internal settings + InternalMongoClientSettings internalSettings = InternalMongoClientSettings.builder().build(); + + assertNotNull(internalSettings.getInternalConnectionPoolSettings(), + "Default internal settings should have connection pool settings"); + assertFalse(internalSettings.isRecordEverything(), + "Default internal settings should have log recordEverything set to false"); + } + + @Test + void testCreateWithCustomInternalConnectionPoolSettings() { + // Demonstrates setting custom internal connection pool settings + InternalConnectionPoolSettings poolSettings = InternalConnectionPoolSettings.builder() + .prestartAsyncWorkManager(false) + .build(); + + InternalMongoClientSettings internalSettings = InternalMongoClientSettings.builder() + .internalConnectionPoolSettings(poolSettings) + .build(); + + assertEquals(poolSettings, internalSettings.getInternalConnectionPoolSettings(), + "Custom connection pool settings should be preserved"); + } + + @Test + void testCreateWithCustomLogRecordingSettings() { + InternalMongoClientSettings internalSettings = InternalMongoClientSettings.builder() + .recordEverything(true) + .build(); + + assertTrue(internalSettings.isRecordEverything()); + } + + @Test + void testGetDefaultsReturnsSameInstance() { + InternalMongoClientSettings defaults1 = InternalMongoClientSettings.getDefaults(); + InternalMongoClientSettings defaults2 = InternalMongoClientSettings.getDefaults(); + assertSame(defaults1, defaults2, "getDefaults() should return the same instance"); + } + + @Test + void testCreateMethodsValidateNullSettings() { + // Verify that null MongoClientSettings is rejected + assertThrows(IllegalArgumentException.class, () -> + InternalMongoClients.create((MongoClientSettings) null, InternalMongoClientSettings.getDefaults())); + + // Verify that null InternalMongoClientSettings is rejected + MongoClientSettings settings = MongoClientSettings.builder().build(); + assertThrows(IllegalArgumentException.class, () -> + InternalMongoClients.create(settings, null)); + } +}