DATAMONGO-2199 - Fix deprecation warnings.

Fix those deprecations with alternatives available. Some still have to remain in code as it is unclear which API to use with the 4.x driver.
Leave some TODOs in the code to find those spots when upgrading to the 4.0 MongoDB Java Driver.

Original pull request: #643.
This commit is contained in:
Christoph Strobl
2019-02-06 10:33:58 +01:00
committed by Mark Paluch
parent 89fde7f8e9
commit f7f004ec8a
34 changed files with 373 additions and 135 deletions

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -26,6 +27,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoCredential;
@@ -78,12 +80,23 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport {
verifyUserNamePresent(userNameAndPassword);
credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0]));
} else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) {
} else if ("MONGODB-CR".equals(authMechanism)) {
verifyUsernameAndPasswordPresent(userNameAndPassword);
verifyDatabasePresent(database);
credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database,
userNameAndPassword[1].toCharArray()));
Method createCRCredentialMethod = ReflectionUtils.findMethod(MongoCredential.class,
"createMongoCRCredential", String.class, String.class, char[].class);
if (createCRCredentialMethod == null) {
throw new IllegalArgumentException("MONGODB-CR is no longer supported.");
}
MongoCredential credential = MongoCredential.class
.cast(ReflectionUtils.invokeMethod(createCRCredentialMethod, null, userNameAndPassword[0], database,
userNameAndPassword[1].toCharArray()));
credentials.add(credential);
} else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) {
verifyUserNamePresent(userNameAndPassword);

View File

@@ -38,11 +38,10 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import com.mongodb.util.JSONParseException;
/**
* Common operations performed on an entity in the context of it's mapping metadata.
*
@@ -165,8 +164,15 @@ class EntityOperations {
try {
return Document.parse(source);
} catch (JSONParseException | org.bson.json.JsonParseException o_O) {
} catch (org.bson.json.JsonParseException o_O) {
throw new MappingException("Could not parse given String to save into a JSON document!", o_O);
} catch (RuntimeException o_O) {
// legacy 3.x exception
if (ClassUtils.matchesTypeName(o_O.getClass(), "JSONParseException")) {
throw new MappingException("Could not parse given String to save into a JSON document!", o_O);
}
throw o_O;
}
}

View File

@@ -41,7 +41,7 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
private static final MongoClientOptions DEFAULT_MONGO_OPTIONS = MongoClientOptions.builder().build();
private @Nullable String description = DEFAULT_MONGO_OPTIONS.getDescription();
private @Nullable String description = DEFAULT_MONGO_OPTIONS.getApplicationName(); // TODO: Mongo Driver 4 - use application name insetad of desription if not available
private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost();
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
@@ -51,14 +51,14 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
private int maxConnectionLifeTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionLifeTime();
private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout();
private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout();
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive();
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive(); // TODO: Mongo Driver 4 - check if available
private @Nullable ReadPreference readPreference = DEFAULT_MONGO_OPTIONS.getReadPreference();
private DBDecoderFactory dbDecoderFactory = DEFAULT_MONGO_OPTIONS.getDbDecoderFactory();
private DBEncoderFactory dbEncoderFactory = DEFAULT_MONGO_OPTIONS.getDbEncoderFactory();
private @Nullable WriteConcern writeConcern = DEFAULT_MONGO_OPTIONS.getWriteConcern();
private @Nullable SocketFactory socketFactory = DEFAULT_MONGO_OPTIONS.getSocketFactory();
private boolean cursorFinalizerEnabled = DEFAULT_MONGO_OPTIONS.isCursorFinalizerEnabled();
private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans();
private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans(); // TODO: Mongo Driver 4 - remove this option
private int heartbeatFrequency = DEFAULT_MONGO_OPTIONS.getHeartbeatFrequency();
private int minHeartbeatFrequency = DEFAULT_MONGO_OPTIONS.getMinHeartbeatFrequency();
private int heartbeatConnectTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatConnectTimeout();
@@ -74,6 +74,7 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
*
* @param description
*/
// TODO: Mongo Driver 4 - deprecate that one and add application name
public void setDescription(@Nullable String description) {
this.description = description;
}
@@ -285,7 +286,7 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
.cursorFinalizerEnabled(cursorFinalizerEnabled) //
.dbDecoderFactory(dbDecoderFactory) //
.dbEncoderFactory(dbEncoderFactory) //
.description(description) //
.applicationName(description) // TODO: Mongo Driver 4 - use applicatoin name if description not available
.heartbeatConnectTimeout(heartbeatConnectTimeout) //
.heartbeatFrequency(heartbeatFrequency) //
.heartbeatSocketTimeout(heartbeatSocketTimeout) //
@@ -297,8 +298,8 @@ public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClie
.readPreference(readPreference) //
.requiredReplicaSetName(requiredReplicaSetName) //
.serverSelectionTimeout(serverSelectionTimeout) //
.socketFactory(socketFactoryToUse) //
.socketKeepAlive(socketKeepAlive) //
.socketFactory(socketFactoryToUse) // TODO: Mongo Driver 4 - remove if not available
.socketKeepAlive(socketKeepAlive) // TODO: Mongo Driver 4 - remove if not available
.socketTimeout(socketTimeout) //
.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) //
.writeConcern(writeConcern).build();

View File

@@ -26,7 +26,6 @@ import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.bson.BSON;
import org.bson.BsonRegularExpression;
import org.bson.Document;
import org.bson.types.Binary;
@@ -67,6 +66,20 @@ public class Criteria implements CriteriaDefinition {
*/
private static final Object NOT_SET = new Object();
private static final int[] FLAG_LOOKUP = new int[Character.MAX_VALUE];
static {
FLAG_LOOKUP['g'] = 256;
FLAG_LOOKUP['i'] = Pattern.CASE_INSENSITIVE;
FLAG_LOOKUP['m'] = Pattern.MULTILINE;
FLAG_LOOKUP['s'] = Pattern.DOTALL;
FLAG_LOOKUP['c'] = Pattern.CANON_EQ;
FLAG_LOOKUP['x'] = Pattern.COMMENTS;
FLAG_LOOKUP['d'] = Pattern.UNIX_LINES;
FLAG_LOOKUP['t'] = Pattern.LITERAL;
FLAG_LOOKUP['u'] = Pattern.UNICODE_CASE;
}
private @Nullable String key;
private List<Criteria> criteriaChain;
private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>();
@@ -450,7 +463,7 @@ public class Criteria implements CriteriaDefinition {
Assert.notNull(regex, "Regex string must not be null!");
return Pattern.compile(regex, options == null ? 0 : BSON.regexFlags(options));
return Pattern.compile(regex, regexFlags(options));
}
/**
@@ -905,6 +918,47 @@ public class Criteria implements CriteriaDefinition {
|| (value instanceof GeoCommand && ((GeoCommand) value).getShape() instanceof GeoJson);
}
/**
* Lookup the MongoDB specific flags for a given regex option string.
*
* @param s the Regex option/flag to look up. Can be {@literal null}.
* @return zero if given {@link String} is {@literal null} or empty.
* @since 2.2
*/
private static int regexFlags(@Nullable String s) {
int flags = 0;
if (s == null) {
return flags;
}
for (final char f : s.toLowerCase().toCharArray()) {
flags |= regexFlag(f);
}
return flags;
}
/**
* Lookup the MongoDB specific flags for a given character.
*
* @param c the Regex option/flag to look up.
* @return
* @throws IllegalArgumentException for unkown flags
* @since 2.2
*/
private static int regexFlag(final char c) {
int flag = FLAG_LOOKUP[c];
if (flag == 0) {
throw new IllegalArgumentException(String.format("Unrecognized flag [%c]", c));
}
return flag;
}
/**
* MongoDB specific <a href="https://docs.mongodb.com/manual/reference/operator/query-bitwise/">bitwise query
* operators</a> like {@code $bitsAllClear, $bitsAllSet,...} for usage with {@link Criteria#bits()} and {@link Query}.

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.core.query;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -22,10 +23,11 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.bson.Document;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriterSettings;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import com.mongodb.util.JSON;
import org.springframework.util.ObjectUtils;
/**
* Utility methods for JSON serialization.
@@ -118,19 +120,34 @@ public abstract class SerializationUtils {
}
try {
return value instanceof Document ? ((Document) value).toJson() : JSON.serialize(value);
String json = value instanceof Document ? ((Document) value).toJson() : serializeValue(value);
return json.replaceAll("\"\\:", "\" :").replaceAll("\\{\"", "{ \""); //.replaceAll("\\]\\}", "] }");
} catch (Exception e) {
if (value instanceof Collection) {
return toString((Collection<?>) value);
} else if (value instanceof Map) {
return toString((Map<?, ?>) value);
} else {
} else if (ObjectUtils.isArray(value)) {
return toString(Arrays.asList(ObjectUtils.toObjectArray(value)));
}
else {
return String.format("{ \"$java\" : %s }", value.toString());
}
}
}
public static String serializeValue(@Nullable Object value) {
if(value == null) {
return "null";
}
String documentJson = new Document("toBeEncoded", value).toJson();
return documentJson.substring(documentJson.indexOf(':') + 1, documentJson.length() - 1).trim();
}
private static String toString(Map<?, ?> source) {
return iterableToDelimitedString(source.entrySet(), "{ ", " }",
entry -> String.format("\"%s\" : %s", entry.getKey(), serializeToJsonSafely(entry.getValue())));

View File

@@ -39,6 +39,7 @@ import org.bson.codecs.UuidCodec;
import org.bson.json.JsonWriter;
import org.bson.types.Binary;
import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.mongodb.repository.query.StringBasedMongoQuery.ParameterBinding;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.EvaluationContext;
@@ -52,7 +53,6 @@ import org.springframework.util.StringUtils;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.util.JSON;
/**
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placeholders within a
@@ -221,7 +221,8 @@ class ExpressionEvaluatingParameterBinder {
return (String) value;
}
return binding.isExpression() ? JSON.serialize(value) : QuotedString.unquote(JSON.serialize(value));
String encodedValue = serialize(value);
return binding.isExpression() ? encodedValue : QuotedString.unquote(encodedValue);
}
return EncodableValue.create(value).encode(codecRegistryProvider, binding.isQuoted());
@@ -660,7 +661,11 @@ class ExpressionEvaluatingParameterBinder {
*/
@Override
public String encode(CodecRegistryProvider provider, boolean quoted) {
return JSON.serialize(this.value);
return serialize(this.value);
}
}
static String serialize(Object value) {
return SerializationUtils.serializeValue(value);
}
}

View File

@@ -270,6 +270,7 @@ public class MongoQueryMethod extends QueryMethod {
}
if (meta.maxScanDocuments() > 0) {
// TODO: Mongo 4 - removal
metaAttributes.setMaxScan(meta.maxScanDocuments());
}
@@ -282,6 +283,8 @@ public class MongoQueryMethod extends QueryMethod {
}
if (meta.snapshot()) {
// TODO: Mongo 4 - removal
metaAttributes.setSnapshot(meta.snapshot());
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.repository.query;
import org.bson.Document;
import org.bson.json.JsonParseException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -31,10 +32,6 @@ import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.util.JSON;
import com.mongodb.util.JSONParseException;
/**
* {@link RepositoryQuery} implementation for Mongo.
*
@@ -114,12 +111,12 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
try {
BasicQuery result = new BasicQuery(query.getQueryObject(), new Document((BasicDBObject) JSON.parse(fieldSpec)));
BasicQuery result = new BasicQuery(query.getQueryObject(), Document.parse(fieldSpec));
result.setSortObject(query.getSortObject());
return result;
} catch (JSONParseException o_O) {
} catch (JsonParseException o_O) {
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod()),
o_O);
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.repository.query;
import org.bson.Document;
import org.bson.json.JsonParseException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -30,8 +31,6 @@ import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.StringUtils;
import com.mongodb.util.JSONParseException;
/**
* Reactive PartTree {@link RepositoryQuery} implementation for Mongo.
*
@@ -110,7 +109,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
return result;
} catch (JSONParseException o_O) {
} catch (JsonParseException o_O) {
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod()),
o_O);
}

View File

@@ -228,6 +228,8 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
String transformedInput = transformQueryAndCollectExpressionParametersIntoBindings(input, bindings);
String parseableInput = makeParameterReferencesParseable(transformedInput);
// Document.parse(parseableInput)
collectParameterReferencesIntoBindings(bindings,
JSON.parse(parseableInput, new LenientPatternDecodingCallback()));

View File

@@ -86,8 +86,12 @@ public class AbstractReactiveMongoConfigurationUnitTests {
assertThat(context.getBean(SimpleReactiveMongoDatabaseFactory.class), is(notNullValue()));
exception.expect(NoSuchBeanDefinitionException.class);
context.getBean(Mongo.class);
context.close();
try {
context.getBean(Mongo.class);
} finally {
context.close();
}
}
@Test // DATAMONGO-1444

View File

@@ -62,7 +62,7 @@ public class MongoClientParserIntegrationTests {
assertThat(factory.getBean("mongo-client-with-host-and-port"), instanceOf(MongoClient.class));
}
@Test // DATAMONGO-1158
@Test // DATAMONGO-1158, DATAMONGO-2199
public void createsMongoClientWithOptionsCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml"));
@@ -75,7 +75,7 @@ public class MongoClientParserIntegrationTests {
MongoClient.class);
assertThat(client.getReadPreference(), is(ReadPreference.secondary()));
assertThat(client.getWriteConcern(), is(WriteConcern.NORMAL));
assertThat(client.getWriteConcern(), is(WriteConcern.UNACKNOWLEDGED));
} finally {
context.close();
}

View File

@@ -23,7 +23,6 @@ import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
@@ -58,23 +57,23 @@ public class MongoDbFactoryParserIntegrationTests {
reader = new XmlBeanDefinitionReader(factory);
}
@Test
@Test // DATAMONGO-2199
public void testWriteConcern() throws Exception {
SimpleMongoDbFactory dbFactory = new SimpleMongoDbFactory(new MongoClient("localhost"), "database");
dbFactory.setWriteConcern(WriteConcern.SAFE);
dbFactory.setWriteConcern(WriteConcern.ACKNOWLEDGED);
dbFactory.getDb();
assertThat(ReflectionTestUtils.getField(dbFactory, "writeConcern"), is((Object) WriteConcern.SAFE));
assertThat(ReflectionTestUtils.getField(dbFactory, "writeConcern"), is((Object) WriteConcern.ACKNOWLEDGED));
}
@Test
@Test // DATAMONGO-2199
public void parsesWriteConcern() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean.xml");
assertWriteConcern(ctx, WriteConcern.SAFE);
assertWriteConcern(ctx, WriteConcern.ACKNOWLEDGED);
}
@Test
@Test // DATAMONGO-2199
public void parsesCustomWriteConcern() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"namespace/db-factory-bean-custom-write-concern.xml");
@@ -89,7 +88,7 @@ public class MongoDbFactoryParserIntegrationTests {
MongoDbFactory factory = ctx.getBean("second", MongoDbFactory.class);
MongoDatabase db = factory.getDb();
assertThat(db.getWriteConcern(), is(WriteConcern.REPLICAS_SAFE));
assertThat(db.getWriteConcern(), is(WriteConcern.W2));
ctx.close();
}

View File

@@ -26,14 +26,15 @@ import com.mongodb.WriteConcern;
* Unit tests for {@link StringToWriteConcernConverter}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class StringToWriteConcernConverterUnitTests {
StringToWriteConcernConverter converter = new StringToWriteConcernConverter();
@Test
@Test // DATAMONGO-2199
public void createsWellKnownConstantsCorrectly() {
assertThat(converter.convert("SAFE"), is(WriteConcern.SAFE));
assertThat(converter.convert("ACKNOWLEDGED"), is(WriteConcern.ACKNOWLEDGED));
}
@Test

View File

@@ -27,6 +27,7 @@ import com.mongodb.WriteConcern;
* Unit tests for {@link WriteConcernPropertyEditor}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class WriteConcernPropertyEditorUnitTests {
@@ -37,11 +38,11 @@ public class WriteConcernPropertyEditorUnitTests {
editor = new WriteConcernPropertyEditor();
}
@Test
@Test // DATAMONGO-2199
public void createsWriteConcernForWellKnownConstants() {
editor.setAsText("SAFE");
assertThat(editor.getValue(), is((Object) WriteConcern.SAFE));
editor.setAsText("JOURNALED");
assertThat(editor.getValue(), is((Object) WriteConcern.JOURNALED));
}
@Test

View File

@@ -26,6 +26,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.query.Update.*;
import com.mongodb.MongoClient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -108,7 +109,6 @@ import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
@@ -160,7 +160,7 @@ public class MongoTemplateTests {
}
@Autowired
public void setMongo(Mongo mongo) throws Exception {
public void setMongoClient(MongoClient mongo) throws Exception {
CustomConversions conversions = new MongoCustomConversions(
Arrays.asList(DateToDateTimeConverter.INSTANCE, DateTimeToDateConverter.INSTANCE));
@@ -2820,9 +2820,9 @@ public class MongoTemplateTests {
assertThat(result, hasSize(2));
assertThat(template.getDb().getCollection("sample").count(
assertThat(template.getDb().getCollection("sample").countDocuments(
new org.bson.Document("field", new org.bson.Document("$in", Arrays.asList("spring", "mongodb")))), is(0L));
assertThat(template.getDb().getCollection("sample").count(new org.bson.Document("field", "data")), is(1L));
assertThat(template.getDb().getCollection("sample").countDocuments(new org.bson.Document("field", "data")), is(1L));
}
@Test // DATAMONGO-1001

View File

@@ -116,7 +116,7 @@ public class MongoTemplateTransactionTests {
assertionList.forEach(it -> {
boolean isPresent = collection.count(Filters.eq("_id", it.getId())) != 0;
boolean isPresent = collection.countDocuments(Filters.eq("_id", it.getId())) != 0;
assertThat(isPresent).isEqualTo(it.shouldBePresent())
.withFailMessage(String.format("After transaction entity %s should %s.", it.getPersistable(),

View File

@@ -140,7 +140,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
when(db.runCommand(any(), any(Class.class))).thenReturn(commandResultDocument);
when(collection.find(any(org.bson.Document.class), any(Class.class))).thenReturn(findIterable);
when(collection.mapReduce(any(), any(), eq(Document.class))).thenReturn(mapReduceIterable);
when(collection.count(any(Bson.class), any(CountOptions.class))).thenReturn(1L);
when(collection.count(any(Bson.class), any(CountOptions.class))).thenReturn(1L); // TODO: MongoDB 4 - fix me decprecated
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
when(collection.withReadPreference(any())).thenReturn(collection);

View File

@@ -41,7 +41,7 @@ public class SerializationUtilsUnitTests {
public void writesSimpleDocument() {
Document document = new Document("foo", "bar");
assertThat(serializeToJsonSafely(document), is(document.toJson()));
assertThat(serializeToJsonSafely(document), is("{ \"foo\" : \"bar\"}"));
}
@Test

View File

@@ -75,7 +75,7 @@ public abstract class AbstractGeoSpatialTests {
@Before
public void setUp() {
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
template.setWriteConcern(WriteConcern.JOURNALED);
createIndex();
addVenues();

View File

@@ -82,7 +82,7 @@ public class GeoJsonTests {
@Before
public void setUp() {
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
template.setWriteConcern(WriteConcern.JOURNALED);
createIndex();
addVenues();

View File

@@ -56,7 +56,7 @@ public class GeoSpatialIndexTests extends AbstractIntegrationTests {
@Before
public void setUp() {
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
template.setWriteConcern(WriteConcern.JOURNALED);
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
}

View File

@@ -47,7 +47,7 @@ public class TextIndexTests extends AbstractIntegrationTests {
@Before
public void setUp() throws Exception {
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
template.setWriteConcern(WriteConcern.JOURNALED);
this.indexOps = template.indexOps(TextIndexedDocumentRoot.class);
}

View File

@@ -21,6 +21,7 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -33,12 +34,10 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.DB;
import org.bson.Document;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* @author Jon Brisbin
@@ -66,8 +65,8 @@ public class GeoIndexedTests {
private void cleanDb() throws UnknownHostException {
Mongo mongo = new MongoClient();
DB db = mongo.getDB(GeoIndexedAppConfig.GEO_DB);
MongoClient mongo = new MongoClient();
MongoDatabase db = mongo.getDatabase(GeoIndexedAppConfig.GEO_DB);
for (String coll : collectionsToDrop) {
db.getCollection(coll).drop();

View File

@@ -90,7 +90,7 @@ public class ApplicationContextEventTests {
applicationContext = new AnnotationConfigApplicationContext(ApplicationContextEventTestsAppConfig.class);
template = applicationContext.getBean(MongoTemplate.class);
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
template.setWriteConcern(WriteConcern.JOURNALED);
listener = applicationContext.getBean(SimpleMappingEventListener.class);
}

View File

@@ -467,11 +467,11 @@ public class UpdateTests {
assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", date)));
}
@Test // DATAMONGO-1777
@Test // DATAMONGO-1777, DATAMONGO-2199
public void toStringShouldPrettyPrintModifiers() {
assertThat(new Update().push("key").atPosition(Position.FIRST).value("Arya").toString()).isEqualTo(
"{ \"$push\" : { \"key\" : { \"$java\" : { \"$position\" : { \"$java\" : { \"$position\" : 0} }, \"$each\" : { \"$java\" : { \"$each\" : [ \"Arya\"]} } } } } }");
"{ \"$push\" : { \"key\" : { \"$java\" : { \"$position\" : { \"$java\" : { \"$position\" : 0} }, \"$each\" : { \"$java\" : { \"$each\" : [ \"Arya\" ] } } } } } }");
}
@Test // DATAMONGO-1777, DATAMONGO-2198

View File

@@ -19,20 +19,12 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.mongodb.client.FindIterable;
import com.mongodb.client.model.CreateCollectionOptions;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.Before;
@@ -53,12 +45,13 @@ import org.springframework.util.StringUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Test class to execute performance tests for plain MongoDB driver usage, {@link MongoTemplate} and the repositories
@@ -76,7 +69,7 @@ public class PerformanceTests {
private static final StopWatch watch = new StopWatch();
private static final Collection<String> IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE",
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED", "W2", "W3");
private static final int COLLECTION_SIZE = 1024-2018 * 1024-2018 * 256; // 256 MB
private static final int COLLECTION_SIZE = 1024 - 2018 * 1024 - 2018 * 256; // 256 MB
private static final Collection<String> COLLECTION_NAMES = Arrays.asList("template", "driver", "person");
MongoClient mongo;
@@ -149,7 +142,7 @@ public class PerformanceTests {
List<Person> persons = new ArrayList<PerformanceTests.Person>();
for (Document document : documents) {
persons.add(Person.from(new BasicDBObject(document)));
persons.add(Person.from(document));
}
return persons;
@@ -182,7 +175,7 @@ public class PerformanceTests {
@Test
public void writeAndRead() throws Exception {
mongo.setWriteConcern(WriteConcern.SAFE);
mongo.setWriteConcern(WriteConcern.ACKNOWLEDGED);
readsAndWrites(NUMBER_OF_PERSONS, ITERATIONS);
}
@@ -257,19 +250,26 @@ public class PerformanceTests {
private void setupCollections() {
DB db = this.mongo.getDB(DATABASE_NAME);
MongoDatabase db = this.mongo.getDatabase(DATABASE_NAME);
for (String collectionName : COLLECTION_NAMES) {
DBCollection collection = db.getCollection(collectionName);
MongoCollection<Document> collection = db.getCollection(collectionName);
collection.drop();
collection.getDB().command(getCreateCollectionCommand(collectionName));
collection.createIndex(new BasicDBObject("firstname", -1));
collection.createIndex(new BasicDBObject("lastname", -1));
CreateCollectionOptions collectionOptions = new CreateCollectionOptions();
collectionOptions.capped(false);
collectionOptions.sizeInBytes(COLLECTION_SIZE);
db.createCollection(collectionName, collectionOptions);
collection.createIndex(new Document("firstname", -1));
collection.createIndex(new Document("lastname", -1));
}
}
private DBObject getCreateCollectionCommand(String name) {
DBObject document = new BasicDBObject();
private Document getCreateCollectionCommand(String name) {
Document document = new Document();
document.put("createCollection", name);
document.put("capped", false);
document.put("size", COLLECTION_SIZE);
@@ -278,10 +278,14 @@ public class PerformanceTests {
private long writingObjectsUsingPlainDriver(int numberOfPersons) {
DBCollection collection = mongo.getDB(DATABASE_NAME).getCollection("driver");
MongoCollection<Document> collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver");
List<Person> persons = getPersonObjects(numberOfPersons);
executeWatched(() -> persons.stream().map(it -> collection.save(new BasicDBObject(it.toDocument()))));
executeWatched(() -> persons.stream().map(Person::toDocument).map(it -> {
collection.insertOne(it);
return true;
}));
return watch.getLastTaskTimeMillis();
}
@@ -308,7 +312,7 @@ public class PerformanceTests {
private long readingUsingPlainDriver() {
executeWatched(() -> toPersons(mongo.getDB(DATABASE_NAME).getCollection("driver").find()));
executeWatched(() -> toPersons(mongo.getDatabase(DATABASE_NAME).getCollection("driver").find()));
return watch.getLastTaskTimeMillis();
}
@@ -329,10 +333,10 @@ public class PerformanceTests {
executeWatched(() -> {
DBCollection collection = mongo.getDB(DATABASE_NAME).getCollection("driver");
MongoCollection<Document> collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver");
BasicDBObject regex = new BasicDBObject("$regex", Pattern.compile(".*1.*"));
BasicDBObject query = new BasicDBObject("addresses.zipCode", regex);
Document regex = new Document("$regex", Pattern.compile(".*1.*"));
Document query = new Document("addresses.zipCode", regex);
return toPersons(collection.find(query));
});
@@ -385,12 +389,13 @@ public class PerformanceTests {
}
}
private static List<Person> toPersons(DBCursor cursor) {
private static List<Person> toPersons(FindIterable<Document> cursor) {
List<Person> persons = new ArrayList<Person>();
while (cursor.hasNext()) {
persons.add(Person.from(cursor.next()));
Iterator<Document> it = cursor.iterator();
while (it.hasNext()) {
persons.add(Person.from(it.next()));
}
return persons;
@@ -410,15 +415,15 @@ public class PerformanceTests {
this.orders = new HashSet<Order>();
}
public static Person from(DBObject source) {
public static Person from(Document source) {
BasicDBList addressesSource = (BasicDBList) source.get("addresses");
List addressesSource = (List) source.get("addresses");
List<Address> addresses = new ArrayList<Address>(addressesSource.size());
for (Object addressSource : addressesSource) {
addresses.add(Address.from((Document) addressSource));
}
BasicDBList ordersSource = (BasicDBList) source.get("orders");
List ordersSource = (List) source.get("orders");
Set<Order> orders = new HashSet<Order>(ordersSource.size());
for (Object orderSource : ordersSource) {
orders.add(Order.from((Document) orderSource));

View File

@@ -196,7 +196,7 @@ public class ReactivePerformanceTests {
@Test // DATAMONGO-1444
public void writeAndRead() throws Exception {
readsAndWrites(NUMBER_OF_PERSONS, ITERATIONS, WriteConcern.SAFE);
readsAndWrites(NUMBER_OF_PERSONS, ITERATIONS, WriteConcern.ACKNOWLEDGED);
}
private void readsAndWrites(int numberOfPersons, int iterations, WriteConcern concern) {

View File

@@ -25,6 +25,7 @@ import java.lang.reflect.Method;
import java.util.List;
import org.bson.Document;
import org.bson.json.JsonParseException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -51,8 +52,6 @@ import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import com.mongodb.util.JSONParseException;
/**
* Unit tests for {@link PartTreeMongoQuery}.
*
@@ -130,7 +129,7 @@ public class PartTreeMongoQueryUnitTests {
public void propagatesRootExceptionForInvalidQuery() {
exception.expect(IllegalStateException.class);
exception.expectCause(is(instanceOf(JSONParseException.class)));
exception.expectCause(is(instanceOf(JsonParseException.class)));
deriveQueryFromMethod("findByAge", 1);
}

View File

@@ -218,7 +218,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
+ Base64Utils.encodeToString(binaryData) + "', '$type' : '" + BSON.B_GENERAL + "'}}");
+ Base64Utils.encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}");
assertThat(query.getQueryObject().toJson(), is(reference.getQueryObject().toJson()));
}

View File

@@ -20,8 +20,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.bson.Document;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -33,6 +35,8 @@ import org.springframework.util.StringUtils;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* {@link CleanMongoDB} is a junit {@link TestRule} implementation to be used as for wiping data from MongoDB instance.
@@ -55,7 +59,7 @@ public class CleanMongoDB implements TestRule {
DATABASE, COLLECTION, INDEX;
}
@SuppressWarnings("serial")//
@SuppressWarnings("serial") //
private Set<String> preserveDatabases = new HashSet<String>() {
{
add("admin");
@@ -274,7 +278,7 @@ public class CleanMongoDB implements TestRule {
continue;
}
DB db = client.getDB(dbName);
MongoDatabase db = client.getDatabase(dbName);
dropCollectionsOrIndexIfRequried(db, initCollectionNames(db));
}
}
@@ -290,13 +294,15 @@ public class CleanMongoDB implements TestRule {
return true;
}
private void dropCollectionsOrIndexIfRequried(DB db, Collection<String> collectionsToUse) {
private void dropCollectionsOrIndexIfRequried(MongoDatabase db, Collection<String> collectionsToUse) {
Collection<String> availableCollections = db.listCollectionNames().into(new LinkedHashSet<>());
for (String collectionName : collectionsToUse) {
if (db.collectionExists(collectionName)) {
if (availableCollections.contains(collectionName)) {
DBCollection collection = db.getCollectionFromString(collectionName);
MongoCollection<Document> collection = db.getCollection(collectionName);
if (collection != null) {
if (types.contains(Struct.COLLECTION)) {
@@ -319,16 +325,16 @@ public class CleanMongoDB implements TestRule {
Collection<String> dbNamesToUse = dbNames;
if (dbNamesToUse.isEmpty()) {
dbNamesToUse = client.getDatabaseNames();
dbNamesToUse = client.listDatabaseNames().into(new LinkedHashSet<>());
}
return dbNamesToUse;
}
private Collection<String> initCollectionNames(DB db) {
private Collection<String> initCollectionNames(MongoDatabase db) {
Collection<String> collectionsToUse = collectionNames;
if (CollectionUtils.isEmpty(collectionsToUse)) {
collectionsToUse = db.getCollectionNames();
collectionsToUse = db.listCollectionNames().into(new LinkedHashSet<>());
}
return collectionsToUse;
}

View File

@@ -18,9 +18,13 @@ package org.springframework.data.mongodb.test.util;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.Description;
@@ -30,9 +34,15 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Block;
import com.mongodb.Function;
import com.mongodb.MongoClient;
import com.mongodb.client.ListCollectionsIterable;
import com.mongodb.client.ListDatabasesIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
/**
* @author Christoph Strobl
@@ -50,35 +60,152 @@ public class CleanMongoDBTests {
private @Mock MongoClient mongoClientMock;
// Some Mock DBs
private @Mock DB db1mock, db2mock;
private @Mock DBCollection db1collection1mock, db1collection2mock, db2collection1mock;
private @Mock MongoDatabase db1mock, db2mock;
private @Mock MongoCollection<Document> db1collection1mock, db1collection2mock, db2collection1mock;
@SuppressWarnings("serial")
@Before
public void setUp() {
// DB setup
when(mongoClientMock.getDatabaseNames()).thenReturn(Arrays.asList("admin", "db1", "db2"));
when(mongoClientMock.getDB(eq("db1"))).thenReturn(db1mock);
when(mongoClientMock.getDB(eq("db2"))).thenReturn(db2mock);
when(mongoClientMock.listDatabaseNames()).thenReturn(new ListDatabasesIterable<String>() {
@Override
public ListDatabasesIterable<String> maxTime(long maxTime, TimeUnit timeUnit) {
return null;
}
// collections have to exist
when(db1mock.collectionExists(anyString())).thenReturn(true);
when(db2mock.collectionExists(anyString())).thenReturn(true);
@Override
public ListDatabasesIterable<String> batchSize(int batchSize) {
return null;
}
// init collection names per database
when(db1mock.getCollectionNames()).thenReturn(new HashSet<String>() {
{
add("db1collection1");
add("db1collection2");
@Override
public ListDatabasesIterable<String> filter(Bson filter) {
return null;
}
@Override
public ListDatabasesIterable<String> nameOnly(Boolean nameOnly) {
return null;
}
@Override
public MongoCursor<String> iterator() {
return null;
}
@Override
public String first() {
return null;
}
@Override
public <U> MongoIterable<U> map(Function<String, U> mapper) {
return null;
}
@Override
public void forEach(Block<? super String> block) {
}
@Override
public <A extends Collection<? super String>> A into(A target) {
return (A) Arrays.asList("admin", "db1", "db2");
}
});
when(mongoClientMock.getDatabase(eq("db1"))).thenReturn(db1mock);
when(mongoClientMock.getDatabase(eq("db2"))).thenReturn(db2mock);
// collections have to exist
when(db1mock.listCollectionNames()).thenReturn(new ListCollectionsIterable<String>() {
@Override
public ListCollectionsIterable<String> filter(Bson filter) {
return null;
}
@Override
public ListCollectionsIterable<String> maxTime(long maxTime, TimeUnit timeUnit) {
return null;
}
@Override
public ListCollectionsIterable<String> batchSize(int batchSize) {
return null;
}
@Override
public MongoCursor<String> iterator() {
return null;
}
@Override
public String first() {
return null;
}
@Override
public <U> MongoIterable<U> map(Function<String, U> mapper) {
return null;
}
@Override
public void forEach(Block<? super String> block) {
}
@Override
public <A extends Collection<? super String>> A into(A target) {
return (A) Arrays.asList("db1collection1", "db1collection2");
}
});
when(db2mock.listCollectionNames()).thenReturn(new ListCollectionsIterable<String>() {
@Override
public ListCollectionsIterable<String> filter(Bson filter) {
return null;
}
@Override
public ListCollectionsIterable<String> maxTime(long maxTime, TimeUnit timeUnit) {
return null;
}
@Override
public ListCollectionsIterable<String> batchSize(int batchSize) {
return null;
}
@Override
public MongoCursor<String> iterator() {
return null;
}
@Override
public String first() {
return null;
}
@Override
public <U> MongoIterable<U> map(Function<String, U> mapper) {
return null;
}
@Override
public void forEach(Block<? super String> block) {
}
@Override
public <A extends Collection<? super String>> A into(A target) {
return (A) Arrays.asList("db2collection1");
}
});
when(db2mock.getCollectionNames()).thenReturn(Collections.singleton("db2collection1"));
// return collections according to names
when(db1mock.getCollectionFromString(eq("db1collection1"))).thenReturn(db1collection1mock);
when(db1mock.getCollectionFromString(eq("db1collection2"))).thenReturn(db1collection2mock);
when(db2mock.getCollectionFromString(eq("db2collection1"))).thenReturn(db2collection1mock);
when(db1mock.getCollection(eq("db1collection1"))).thenReturn(db1collection1mock);
when(db1mock.getCollection(eq("db1collection2"))).thenReturn(db1collection2mock);
when(db2mock.getCollection(eq("db2collection1"))).thenReturn(db2collection1mock);
cleaner = new CleanMongoDB(mongoClientMock);
}

View File

@@ -11,7 +11,7 @@
<mongo:client-options />
</mongo:mongo-client>
<mongo:db-factory id="second" write-concern="REPLICAS_SAFE" />
<mongo:db-factory id="second" write-concern="W2" />
<!-- now part of the namespace
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
@@ -23,4 +23,4 @@
</bean>
-->
</beans>
</beans>

View File

@@ -8,7 +8,7 @@
<mongo:mongo-client id="mongo-client-with-host-and-port" host="localhost" port="42" />
<mongo:mongo-client id="mongo-client-with-options-for-write-concern-and-read-preference">
<mongo:client-options write-concern="NORMAL" read-preference="SECONDARY" />
<mongo:client-options write-concern="UNACKNOWLEDGED" read-preference="SECONDARY" />
</mongo:mongo-client>
<mongo:mongo-client id="mongo-client-with-credentials" credentials="jon:warg@snow?uri.authMechanism=PLAIN" />