Browse Source

Remove MongoDB driver 4 compatibility.

Closes: #4886
pull/4976/head
Christoph Strobl 8 months ago committed by Mark Paluch
parent
commit
6531bc792b
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
  2. 18
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java
  3. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  4. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
  5. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/MongoClientEncryption.java
  6. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java
  7. 18
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java
  8. 19
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
  9. 19
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java
  10. 13
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java
  11. 417
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java
  12. 4
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java
  13. 3
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java
  14. 6
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java
  15. 4
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/IndexUnitTests.java
  16. 6
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBTests.java
  17. 3
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java
  18. 49
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapterUnitTests.java

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java

@ -18,11 +18,9 @@ package org.springframework.data.mongodb.core; @@ -18,11 +18,9 @@ package org.springframework.data.mongodb.core;
import java.util.concurrent.TimeUnit;
import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@ -90,9 +88,6 @@ abstract class IndexConverters { @@ -90,9 +88,6 @@ abstract class IndexConverters {
if (indexOptions.containsKey("bits")) {
ops = ops.bits((Integer) indexOptions.get("bits"));
}
if (indexOptions.containsKey("bucketSize")) {
MongoCompatibilityAdapter.indexOptionsAdapter(ops).setBucketSize(((Number) indexOptions.get("bucketSize")).doubleValue());
}
if (indexOptions.containsKey("default_language")) {
ops = ops.defaultLanguage(indexOptions.get("default_language").toString());
}

18
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java

@ -25,9 +25,7 @@ import javax.net.ssl.SSLContext; @@ -25,9 +25,7 @@ import javax.net.ssl.SSLContext;
import org.bson.UuidRepresentation;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@ -57,8 +55,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli @@ -57,8 +55,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli
private CodecRegistry codecRegistry = DEFAULT_MONGO_SETTINGS.getCodecRegistry();
@Nullable private Object streamFactoryFactory = MongoCompatibilityAdapter
.clientSettingsAdapter(DEFAULT_MONGO_SETTINGS).getStreamFactoryFactory();
@Nullable private TransportSettings transportSettings;
private ReadPreference readPreference = DEFAULT_MONGO_SETTINGS.getReadPreference();
@ -371,16 +367,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli @@ -371,16 +367,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli
this.readPreference = readPreference;
}
/**
* @param streamFactoryFactory
* @deprecated since 4.3, will be removed in the MongoDB 5.0 driver in favor of
* {@code com.mongodb.connection.TransportSettings}.
*/
@Deprecated(since = "4.3")
public void setStreamFactoryFactory(Object streamFactoryFactory) {
this.streamFactoryFactory = streamFactoryFactory;
}
public void setTransportSettings(@Nullable TransportSettings transportSettings) {
this.transportSettings = transportSettings;
}
@ -492,10 +478,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli @@ -492,10 +478,6 @@ public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoCli
builder.transportSettings(transportSettings);
}
if (streamFactoryFactory != null) {
MongoCompatibilityAdapter.clientSettingsBuilderAdapter(builder).setStreamFactoryFactory(streamFactoryFactory);
}
if (retryReads != null) {
builder = builder.retryReads(retryReads);
}

10
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -107,7 +107,6 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition; @@ -107,7 +107,6 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.springframework.data.projection.EntityProjection;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Optionals;
@ -728,7 +727,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -728,7 +727,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return execute(db -> {
for (String name : MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(db).listCollectionNames()) {
for (String name : db.listCollectionNames()) {
if (name.equals(collectionName)) {
return true;
}
@ -1980,11 +1979,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -1980,11 +1979,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
mapReduce = mapReduce.jsMode(mapReduceOptions.getJavaScriptMode());
}
if (mapReduceOptions.getOutputSharded().isPresent()) {
MongoCompatibilityAdapter.mapReduceIterableAdapter(mapReduce)
.sharded(mapReduceOptions.getOutputSharded().get());
}
if (StringUtils.hasText(mapReduceOptions.getOutputCollection()) && !mapReduceOptions.usesInlineOutput()) {
mapReduce = mapReduce.collectionName(mapReduceOptions.getOutputCollection())
@ -2367,7 +2361,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -2367,7 +2361,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
public Set<String> getCollectionNames() {
return execute(db -> {
Set<String> result = new LinkedHashSet<>();
for (String name : MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(db).listCollectionNames()) {
for (String name : db.listCollectionNames()) {
result.add(name);
}
return result;

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

@ -118,7 +118,6 @@ import org.springframework.data.mongodb.core.query.NearQuery; @@ -118,7 +118,6 @@ import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.springframework.data.projection.EntityProjection;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
@ -739,7 +738,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @@ -739,7 +738,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
@Override
public Mono<Boolean> collectionExists(String collectionName) {
return createMono(db -> Flux.from(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames()) //
return createMono(db -> Flux.from(db.listCollectionNames()) //
.filter(s -> s.equals(collectionName)) //
.map(s -> true) //
.single(false));
@ -787,7 +786,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @@ -787,7 +786,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
@Override
public Flux<String> getCollectionNames() {
return createFlux(db -> MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames());
return createFlux(db -> db.listCollectionNames());
}
public Mono<MongoDatabase> getMongoDatabase() {
@ -2176,10 +2175,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @@ -2176,10 +2175,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
publisher = publisher.jsMode(options.getJavaScriptMode());
}
if (options.getOutputSharded().isPresent()) {
MongoCompatibilityAdapter.mapReducePublisherAdapter(publisher).sharded(options.getOutputSharded().get());
}
if (StringUtils.hasText(options.getOutputCollection()) && !options.usesInlineOutput()) {
publisher = publisher.collectionName(options.getOutputCollection()).action(options.getMapReduceAction());

4
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/MongoClientEncryption.java

@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
*/
package org.springframework.data.mongodb.core.encryption;
import static org.springframework.data.mongodb.util.MongoCompatibilityAdapter.rangeOptionsAdapter;
import java.util.Map;
import java.util.function.Supplier;
@ -124,7 +122,7 @@ public class MongoClientEncryption implements Encryption<BsonValue, BsonBinary> @@ -124,7 +122,7 @@ public class MongoClientEncryption implements Encryption<BsonValue, BsonBinary>
Assert.isInstanceOf(Integer.class, trimFactor, () -> String
.format("Expected to find a %s but it turned out to be %s.", Integer.class, trimFactor.getClass()));
rangeOptionsAdapter(encryptionRangeOptions).trimFactor((Integer) trimFactor);
encryptionRangeOptions.trimFactor((Integer) trimFactor);
}
if (attributes.containsKey("sparsity")) {

10
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java

@ -114,16 +114,6 @@ public @interface GeoSpatialIndexed { @@ -114,16 +114,6 @@ public @interface GeoSpatialIndexed {
*/
GeoSpatialIndexType type() default GeoSpatialIndexType.GEO_2D;
/**
* The bucket size for {@link GeoSpatialIndexType#GEO_HAYSTACK} indexes, in coordinate units.
*
* @since 1.4
* @return {@literal 1.0} by default.
* @deprecated since MongoDB server version 4.4
*/
@Deprecated
double bucketSize() default 1.0;
/**
* The name of the additional field to use for {@link GeoSpatialIndexType#GEO_HAYSTACK} indexes
*

18
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java

@ -19,7 +19,6 @@ import java.util.Optional; @@ -19,7 +19,6 @@ import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -41,7 +40,6 @@ public class GeospatialIndex implements IndexDefinition { @@ -41,7 +40,6 @@ public class GeospatialIndex implements IndexDefinition {
private @Nullable Integer max;
private @Nullable Integer bits;
private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
private Double bucketSize = MongoClientVersion.isVersion5orNewer() ? null : 1.0;
private @Nullable String additionalField;
private Optional<IndexFilter> filter = Optional.empty();
private Optional<Collation> collation = Optional.empty();
@ -107,17 +105,6 @@ public class GeospatialIndex implements IndexDefinition { @@ -107,17 +105,6 @@ public class GeospatialIndex implements IndexDefinition {
return this;
}
/**
* @param bucketSize
* @return this.
* @deprecated since MongoDB server version 4.4
*/
@Deprecated
public GeospatialIndex withBucketSize(double bucketSize) {
this.bucketSize = bucketSize;
return this;
}
/**
* @param fieldName
* @return this.
@ -203,14 +190,9 @@ public class GeospatialIndex implements IndexDefinition { @@ -203,14 +190,9 @@ public class GeospatialIndex implements IndexDefinition {
break;
case GEO_2DSPHERE:
break;
case GEO_HAYSTACK:
if (bucketSize != null) {
document.put("bucketSize", bucketSize);
}
break;
}

19
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java

@ -25,7 +25,6 @@ import java.util.HashSet; @@ -25,7 +25,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@ -55,7 +54,6 @@ import org.springframework.data.mongodb.core.query.Collation; @@ -55,7 +54,6 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.mongodb.util.DurationUtil;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.data.mongodb.util.spel.ExpressionUtils;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.util.TypeInformation;
@ -711,23 +709,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { @@ -711,23 +709,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
.named(pathAwareIndexName(index.name(), dotPath, persistentProperty.getOwner(), persistentProperty));
}
if (MongoClientVersion.isVersion5orNewer()) {
Optional<Double> defaultBucketSize = MergedAnnotation.of(GeoSpatialIndexed.class).getDefaultValue("bucketSize",
Double.class);
if (!defaultBucketSize.isPresent() || index.bucketSize() != defaultBucketSize.get()) {
indexDefinition.withBucketSize(index.bucketSize());
} else {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(
"GeoSpatialIndexed.bucketSize no longer supported by Mongo Client 5 or newer. Ignoring bucketSize for path %s."
.formatted(dotPath));
}
}
} else {
indexDefinition.withBucketSize(index.bucketSize());
}
indexDefinition.typed(index.type()).withAdditionalField(index.additionalField());
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);

19
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java

@ -45,7 +45,6 @@ public class MapReduceOptions { @@ -45,7 +45,6 @@ public class MapReduceOptions {
private Boolean verbose = Boolean.TRUE;
private @Nullable Integer limit;
private Optional<Boolean> outputSharded = Optional.empty();
private Optional<String> finalizeFunction = Optional.empty();
private Optional<Collation> collation = Optional.empty();
@ -152,19 +151,6 @@ public class MapReduceOptions { @@ -152,19 +151,6 @@ public class MapReduceOptions {
return this;
}
/**
* If true and combined with an output mode that writes to a collection, the output collection will be sharded using
* the _id field. For MongoDB 1.9+
*
* @param outputShared if true, output will be sharded based on _id key.
* @return MapReduceOptions so that methods can be chained in a fluent API style
*/
public MapReduceOptions outputSharded(boolean outputShared) {
this.outputSharded = Optional.of(outputShared);
return this;
}
/**
* Sets the finalize function
*
@ -245,10 +231,6 @@ public class MapReduceOptions { @@ -245,10 +231,6 @@ public class MapReduceOptions {
return this.outputDatabase;
}
public Optional<Boolean> getOutputSharded() {
return this.outputSharded;
}
public Map<String, Object> getScopeVariables() {
return this.scopeVariables;
}
@ -336,7 +318,6 @@ public class MapReduceOptions { @@ -336,7 +318,6 @@ public class MapReduceOptions {
}
outputDatabase.ifPresent(val -> out.append("db", val));
outputSharded.ifPresent(val -> out.append("sharded", val));
return out;
}

13
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java

@ -17,10 +17,7 @@ package org.springframework.data.mongodb.observability; @@ -17,10 +17,7 @@ package org.springframework.data.mongodb.observability;
import io.micrometer.common.KeyValues;
import java.net.InetSocketAddress;
import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.springframework.util.ObjectUtils;
import com.mongodb.ConnectionString;
@ -78,16 +75,6 @@ class DefaultMongoHandlerObservationConvention implements MongoHandlerObservatio @@ -78,16 +75,6 @@ class DefaultMongoHandlerObservationConvention implements MongoHandlerObservatio
keyValues = keyValues.and(LowCardinalityCommandKeyNames.NET_TRANSPORT.withValue("IP.TCP"),
LowCardinalityCommandKeyNames.NET_PEER_NAME.withValue(serverAddress.getHost()),
LowCardinalityCommandKeyNames.NET_PEER_PORT.withValue("" + serverAddress.getPort()));
InetSocketAddress socketAddress = MongoCompatibilityAdapter.serverAddressAdapter(serverAddress)
.getSocketAddress();
if (socketAddress != null) {
keyValues = keyValues.and(
LowCardinalityCommandKeyNames.NET_SOCK_PEER_ADDR.withValue(socketAddress.getHostName()),
LowCardinalityCommandKeyNames.NET_SOCK_PEER_PORT.withValue("" + socketAddress.getPort()));
}
}
ConnectionId connectionId = connectionDescription.getConnectionId();

417
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java

@ -1,417 +0,0 @@ @@ -1,417 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.util;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.ServerAddress;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MapReduceIterable;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.vault.RangeOptions;
import com.mongodb.reactivestreams.client.MapReducePublisher;
/**
* Compatibility adapter to bridge functionality across different MongoDB driver versions.
* <p>
* This class is for internal use within the framework and should not be used by applications.
*
* @author Christoph Strobl
* @author Ross Lawley
* @since 4.3
*/
public class MongoCompatibilityAdapter {
private static final String NO_LONGER_SUPPORTED = "%s is no longer supported on Mongo Client 5 or newer";
private static final String NOT_SUPPORTED_ON_4 = "%s is not supported on Mongo Client 4";
private static final @Nullable Method getStreamFactoryFactory = ReflectionUtils.findMethod(MongoClientSettings.class,
"getStreamFactoryFactory");
private static final @Nullable Method setBucketSize = ReflectionUtils.findMethod(IndexOptions.class, "bucketSize",
Double.class);
private static final @Nullable Method setTrimFactor;
static {
// method name changed in between
Method trimFactor = ReflectionUtils.findMethod(RangeOptions.class, "setTrimFactor", Integer.class);
if (trimFactor != null) {
setTrimFactor = trimFactor;
} else {
setTrimFactor = ReflectionUtils.findMethod(RangeOptions.class, "trimFactor", Integer.class);
}
}
/**
* Return a compatibility adapter for {@link MongoClientSettings.Builder}.
*
* @param builder
* @return
*/
public static ClientSettingsBuilderAdapter clientSettingsBuilderAdapter(MongoClientSettings.Builder builder) {
return new MongoStreamFactoryFactorySettingsConfigurer(builder)::setStreamFactory;
}
/**
* Return a compatibility adapter for {@link MongoClientSettings}.
*
* @param clientSettings
* @return
*/
public static ClientSettingsAdapter clientSettingsAdapter(MongoClientSettings clientSettings) {
return new ClientSettingsAdapter() {
@Override
public <T> T getStreamFactoryFactory() {
if (MongoClientVersion.isVersion5orNewer() || getStreamFactoryFactory == null) {
return null;
}
return (T) ReflectionUtils.invokeMethod(getStreamFactoryFactory, clientSettings);
}
};
}
/**
* Return a compatibility adapter for {@link IndexOptions}.
*
* @param options
* @return
*/
public static IndexOptionsAdapter indexOptionsAdapter(IndexOptions options) {
return bucketSize -> {
if (MongoClientVersion.isVersion5orNewer() || setBucketSize == null) {
throw new UnsupportedOperationException(NO_LONGER_SUPPORTED.formatted("IndexOptions.bucketSize"));
}
ReflectionUtils.invokeMethod(setBucketSize, options, bucketSize);
};
}
/**
* Return a compatibility adapter for {@code MapReduceIterable}.
*
* @param iterable
* @return
*/
@SuppressWarnings("deprecation")
public static MapReduceIterableAdapter mapReduceIterableAdapter(Object iterable) {
return sharded -> {
if (MongoClientVersion.isVersion5orNewer()) {
throw new UnsupportedOperationException(NO_LONGER_SUPPORTED.formatted("sharded"));
}
// Use MapReduceIterable to avoid package-protected access violations to
// com.mongodb.client.internal.MapReduceIterableImpl
Method shardedMethod = ReflectionUtils.findMethod(MapReduceIterable.class, "sharded", boolean.class);
ReflectionUtils.invokeMethod(shardedMethod, iterable, sharded);
};
}
/**
* Return a compatibility adapter for {@link RangeOptions}.
*
* @param options
* @return
*/
public static RangeOptionsAdapter rangeOptionsAdapter(RangeOptions options) {
return trimFactor -> {
if (!MongoClientVersion.isVersion5orNewer() || setTrimFactor == null) {
throw new UnsupportedOperationException(NOT_SUPPORTED_ON_4.formatted("RangeOptions.trimFactor"));
}
ReflectionUtils.invokeMethod(setTrimFactor, options, trimFactor);
};
}
/**
* Return a compatibility adapter for {@code MapReducePublisher}.
*
* @param publisher
* @return
*/
@SuppressWarnings("deprecation")
public static MapReducePublisherAdapter mapReducePublisherAdapter(Object publisher) {
return sharded -> {
if (MongoClientVersion.isVersion5orNewer()) {
throw new UnsupportedOperationException(NO_LONGER_SUPPORTED.formatted("sharded"));
}
// Use MapReducePublisher to avoid package-protected access violations to MapReducePublisherImpl
Method shardedMethod = ReflectionUtils.findMethod(MapReducePublisher.class, "sharded", boolean.class);
ReflectionUtils.invokeMethod(shardedMethod, publisher, sharded);
};
}
/**
* Return a compatibility adapter for {@link ServerAddress}.
*
* @param serverAddress
* @return
*/
public static ServerAddressAdapter serverAddressAdapter(ServerAddress serverAddress) {
return () -> {
if (MongoClientVersion.isVersion5orNewer()) {
return null;
}
Method serverAddressMethod = ReflectionUtils.findMethod(ServerAddress.class, "getSocketAddress");
Object value = ReflectionUtils.invokeMethod(serverAddressMethod, serverAddress);
return value != null ? InetSocketAddress.class.cast(value) : null;
};
}
public static MongoDatabaseAdapterBuilder mongoDatabaseAdapter() {
return MongoDatabaseAdapter::new;
}
public static ReactiveMongoDatabaseAdapterBuilder reactiveMongoDatabaseAdapter() {
return ReactiveMongoDatabaseAdapter::new;
}
public interface IndexOptionsAdapter {
void setBucketSize(double bucketSize);
}
public interface ClientSettingsAdapter {
@Nullable
<T> T getStreamFactoryFactory();
}
public interface ClientSettingsBuilderAdapter {
<T> void setStreamFactoryFactory(T streamFactory);
}
public interface MapReduceIterableAdapter {
void sharded(boolean sharded);
}
public interface MapReducePublisherAdapter {
void sharded(boolean sharded);
}
public interface ServerAddressAdapter {
@Nullable
InetSocketAddress getSocketAddress();
}
public interface MongoDatabaseAdapterBuilder {
MongoDatabaseAdapter forDb(com.mongodb.client.MongoDatabase db);
}
public interface RangeOptionsAdapter {
void trimFactor(Integer trimFactor);
}
@SuppressWarnings({ "unchecked", "DataFlowIssue" })
public static class MongoDatabaseAdapter {
@Nullable //
private static final Method LIST_COLLECTION_NAMES_METHOD;
@Nullable //
private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION;
private static final Class<?> collectionNamesReturnType;
private final MongoDatabase db;
static {
if (MongoClientVersion.isSyncClientPresent()) {
LIST_COLLECTION_NAMES_METHOD = ReflectionUtils.findMethod(MongoDatabase.class, "listCollectionNames");
LIST_COLLECTION_NAMES_METHOD_SESSION = ReflectionUtils.findMethod(MongoDatabase.class, "listCollectionNames",
ClientSession.class);
if (MongoClientVersion.isVersion5orNewer()) {
try {
collectionNamesReturnType = ClassUtils.forName("com.mongodb.client.ListCollectionNamesIterable",
MongoDatabaseAdapter.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to load com.mongodb.client.ListCollectionNamesIterable", e);
}
} else {
try {
collectionNamesReturnType = ClassUtils.forName("com.mongodb.client.MongoIterable",
MongoDatabaseAdapter.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to load com.mongodb.client.ListCollectionNamesIterable", e);
}
}
} else {
LIST_COLLECTION_NAMES_METHOD = null;
LIST_COLLECTION_NAMES_METHOD_SESSION = null;
collectionNamesReturnType = Object.class;
}
}
public MongoDatabaseAdapter(MongoDatabase db) {
this.db = db;
}
public Class<? extends MongoIterable<String>> collectionNameIterableType() {
return (Class<? extends MongoIterable<String>>) collectionNamesReturnType;
}
public MongoIterable<String> listCollectionNames() {
Assert.state(LIST_COLLECTION_NAMES_METHOD != null, "No method listCollectionNames present for %s".formatted(db));
return (MongoIterable<String>) ReflectionUtils.invokeMethod(LIST_COLLECTION_NAMES_METHOD, db);
}
public MongoIterable<String> listCollectionNames(ClientSession clientSession) {
Assert.state(LIST_COLLECTION_NAMES_METHOD != null,
"No method listCollectionNames(ClientSession) present for %s".formatted(db));
return (MongoIterable<String>) ReflectionUtils.invokeMethod(LIST_COLLECTION_NAMES_METHOD_SESSION, db,
clientSession);
}
}
public interface ReactiveMongoDatabaseAdapterBuilder {
ReactiveMongoDatabaseAdapter forDb(com.mongodb.reactivestreams.client.MongoDatabase db);
}
@SuppressWarnings({ "unchecked", "DataFlowIssue" })
public static class ReactiveMongoDatabaseAdapter {
@Nullable //
private static final Method LIST_COLLECTION_NAMES_METHOD;
@Nullable //
private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION;
private static final Class<?> collectionNamesReturnType;
private final com.mongodb.reactivestreams.client.MongoDatabase db;
static {
if (MongoClientVersion.isReactiveClientPresent()) {
LIST_COLLECTION_NAMES_METHOD = ReflectionUtils
.findMethod(com.mongodb.reactivestreams.client.MongoDatabase.class, "listCollectionNames");
LIST_COLLECTION_NAMES_METHOD_SESSION = ReflectionUtils.findMethod(
com.mongodb.reactivestreams.client.MongoDatabase.class, "listCollectionNames",
com.mongodb.reactivestreams.client.ClientSession.class);
if (MongoClientVersion.isVersion5orNewer()) {
try {
collectionNamesReturnType = ClassUtils.forName(
"com.mongodb.reactivestreams.client.ListCollectionNamesPublisher",
ReactiveMongoDatabaseAdapter.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("com.mongodb.reactivestreams.client.ListCollectionNamesPublisher", e);
}
} else {
try {
collectionNamesReturnType = ClassUtils.forName("org.reactivestreams.Publisher",
ReactiveMongoDatabaseAdapter.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("org.reactivestreams.Publisher", e);
}
}
} else {
LIST_COLLECTION_NAMES_METHOD = null;
LIST_COLLECTION_NAMES_METHOD_SESSION = null;
collectionNamesReturnType = Object.class;
}
}
ReactiveMongoDatabaseAdapter(com.mongodb.reactivestreams.client.MongoDatabase db) {
this.db = db;
}
public Class<? extends Publisher<String>> collectionNamePublisherType() {
return (Class<? extends Publisher<String>>) collectionNamesReturnType;
}
public Publisher<String> listCollectionNames() {
Assert.state(LIST_COLLECTION_NAMES_METHOD != null, "No method listCollectionNames present for %s".formatted(db));
return (Publisher<String>) ReflectionUtils.invokeMethod(LIST_COLLECTION_NAMES_METHOD, db);
}
public Publisher<String> listCollectionNames(com.mongodb.reactivestreams.client.ClientSession clientSession) {
Assert.state(LIST_COLLECTION_NAMES_METHOD != null,
"No method listCollectionNames(ClientSession) present for %s".formatted(db));
return (Publisher<String>) ReflectionUtils.invokeMethod(LIST_COLLECTION_NAMES_METHOD_SESSION, db, clientSession);
}
}
static class MongoStreamFactoryFactorySettingsConfigurer {
private static final Log logger = LogFactory.getLog(MongoStreamFactoryFactorySettingsConfigurer.class);
private static final String STREAM_FACTORY_NAME = "com.mongodb.connection.StreamFactoryFactory";
private static final boolean STREAM_FACTORY_PRESENT = ClassUtils.isPresent(STREAM_FACTORY_NAME,
MongoCompatibilityAdapter.class.getClassLoader());
private final MongoClientSettings.Builder settingsBuilder;
static boolean isStreamFactoryPresent() {
return STREAM_FACTORY_PRESENT;
}
public MongoStreamFactoryFactorySettingsConfigurer(Builder settingsBuilder) {
this.settingsBuilder = settingsBuilder;
}
void setStreamFactory(Object streamFactory) {
if (MongoClientVersion.isVersion5orNewer() && isStreamFactoryPresent()) {
logger.warn("StreamFactoryFactory is no longer available. Use TransportSettings instead.");
return;
}
try {
Class<?> streamFactoryType = ClassUtils.forName(STREAM_FACTORY_NAME, streamFactory.getClass().getClassLoader());
if (!ClassUtils.isAssignable(streamFactoryType, streamFactory.getClass())) {
throw new IllegalArgumentException("Expected %s but found %s".formatted(streamFactoryType, streamFactory));
}
Method setter = ReflectionUtils.findMethod(settingsBuilder.getClass(), "streamFactoryFactory",
streamFactoryType);
if (setter != null) {
ReflectionUtils.invokeMethod(setter, settingsBuilder, streamFactoryType.cast(streamFactory));
}
} catch (ReflectiveOperationException e) {
throw new IllegalArgumentException("Cannot set StreamFactoryFactory for %s".formatted(settingsBuilder), e);
}
}
}
}

4
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java

@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*;
import java.lang.reflect.Proxy;
import com.mongodb.reactivestreams.client.ListCollectionNamesPublisher;
import org.bson.Document;
import org.bson.codecs.BsonValueCodec;
import org.bson.codecs.configuration.CodecRegistry;
@ -58,7 +59,6 @@ import com.mongodb.reactivestreams.client.MapReducePublisher; @@ -58,7 +59,6 @@ import com.mongodb.reactivestreams.client.MapReducePublisher;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
/**
* Unit tests for {@link ReactiveSessionBoundMongoTemplate}.
@ -94,7 +94,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests { @@ -94,7 +94,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests {
@Before
public void setUp() {
mock(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(database).collectionNamePublisherType());
mock(ListCollectionNamesPublisher.class);
when(client.getDatabase(anyString())).thenReturn(database);
when(codecRegistry.get(any(Class.class))).thenReturn(new BsonValueCodec());
when(database.getCodecRegistry()).thenReturn(codecRegistry);

3
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java

@ -49,7 +49,6 @@ import com.mongodb.client.model.CountOptions; @@ -49,7 +49,6 @@ import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.UpdateOptions;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
/**
* Unit test for {@link SessionBoundMongoTemplate} making sure a proxied {@link MongoCollection} and
@ -90,7 +89,7 @@ public class SessionBoundMongoTemplateUnitTests { @@ -90,7 +89,7 @@ public class SessionBoundMongoTemplateUnitTests {
@Before
public void setUp() {
collectionNamesIterable = mock(MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(database).collectionNameIterableType());
collectionNamesIterable = mock(ListCollectionNamesIterable.class);
when(client.getDatabase(anyString())).thenReturn(database);
when(codecRegistry.get(any(Class.class))).thenReturn(new BsonValueCodec());
when(database.getCodecRegistry()).thenReturn(codecRegistry);

6
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java

@ -31,7 +31,6 @@ import org.junit.Test; @@ -31,7 +31,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.core.annotation.AliasFor;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
@ -506,7 +505,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -506,7 +505,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
assertThat(indexDefinition.getIndexKeys()).containsEntry("location", "geoHaystack").containsEntry("What light?",
1);
assertThat(indexDefinition.getIndexOptions()).containsEntry("name", "my_geo_index_name")
.containsEntry("bucketSize", 2.0);
.doesNotContainKey("bucketSize");
}
@Test // DATAMONGO-2112
@ -558,9 +557,6 @@ public class MongoPersistentEntityIndexResolverUnitTests { @@ -558,9 +557,6 @@ public class MongoPersistentEntityIndexResolverUnitTests {
@AliasFor(annotation = GeoSpatialIndexed.class, attribute = "additionalField")
String theAdditionalFieldINeedToDefine() default "What light?";
@AliasFor(annotation = GeoSpatialIndexed.class, attribute = "bucketSize")
double size() default 2;
@AliasFor(annotation = GeoSpatialIndexed.class, attribute = "type")
GeoSpatialIndexType indexType() default GeoSpatialIndexType.GEO_HAYSTACK;
}

4
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/IndexUnitTests.java

@ -80,9 +80,9 @@ public class IndexUnitTests { @@ -80,9 +80,9 @@ public class IndexUnitTests {
public void testGeospatialIndexGeoHaystack() {
GeospatialIndex i = new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_HAYSTACK)
.withAdditionalField("name").withBucketSize(40);
.withAdditionalField("name");
assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"location\" : \"geoHaystack\" , \"name\" : 1}"));
assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ \"bucketSize\" : 40.0}"));
assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ }"));
}
@Test

6
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBTests.java

@ -32,8 +32,8 @@ import org.mockito.junit.jupiter.MockitoExtension; @@ -32,8 +32,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import com.mongodb.client.ListCollectionNamesIterable;
import com.mongodb.client.ListDatabasesIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
@ -74,11 +74,11 @@ class CleanMongoDBTests { @@ -74,11 +74,11 @@ class CleanMongoDBTests {
when(mongoClientMock.getDatabase(eq("db2"))).thenReturn(db2mock);
// collections have to exist
MongoIterable<String> collectionIterable = mock(MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(db1mock).collectionNameIterableType());
MongoIterable<String> collectionIterable = mock(ListCollectionNamesIterable.class);
when(collectionIterable.into(any(Collection.class))).thenReturn(Arrays.asList("db1collection1", "db1collection2"));
doReturn(collectionIterable).when(db1mock).listCollectionNames();
MongoIterable<String> collectionIterable2 = mock(MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(db2mock).collectionNameIterableType());
MongoIterable<String> collectionIterable2 = mock(ListCollectionNamesIterable.class);
when(collectionIterable2.into(any(Collection.class))).thenReturn(Collections.singletonList("db2collection1"));
doReturn(collectionIterable2).when(db2mock).listCollectionNames();

3
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java

@ -28,7 +28,6 @@ import org.springframework.context.ApplicationContext; @@ -28,7 +28,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import com.mongodb.MongoWriteException;
@ -96,7 +95,7 @@ public class MongoTestTemplate extends MongoTemplate { @@ -96,7 +95,7 @@ public class MongoTestTemplate extends MongoTemplate {
}
public void flushDatabase() {
flush(MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(getDb()).listCollectionNames());
flush(getDb().listCollectionNames());
}
public void flush(Iterable<String> collections) {

49
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapterUnitTests.java

@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.util;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.test.util.ExcludeReactiveClientFromClassPath;
import org.springframework.data.mongodb.test.util.ExcludeSyncClientFromClassPath;
import org.springframework.util.ClassUtils;
/**
* @author Christoph Strobl
*/
class MongoCompatibilityAdapterUnitTests {
@Test // GH-4578
@ExcludeReactiveClientFromClassPath
void returnsListCollectionNameIterableTypeCorrectly() {
String expectedType = MongoClientVersion.isVersion5orNewer() ? "ListCollectionNamesIterable" : "MongoIterable";
assertThat(MongoCompatibilityAdapter.mongoDatabaseAdapter().forDb(null).collectionNameIterableType())
.satisfies(type -> assertThat(ClassUtils.getShortName(type)).isEqualTo(expectedType));
}
@Test // GH-4578
@ExcludeSyncClientFromClassPath
void returnsListCollectionNamePublisherTypeCorrectly() {
String expectedType = MongoClientVersion.isVersion5orNewer() ? "ListCollectionNamesPublisher" : "Publisher";
assertThat(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(null).collectionNamePublisherType())
.satisfies(type -> assertThat(ClassUtils.getShortName(type)).isEqualTo(expectedType));
}
}
Loading…
Cancel
Save