From 15f2e49e346a8ff529d8e4226c26244ebd48bd35 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 12 Feb 2015 12:01:48 +0100 Subject: [PATCH 1/8] DATAMONGO-1158 - Add Support for mongo-java-driver 3.0 Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb-log4j/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index d5c7aaf0c4..c8ce08fa9d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index c7610beee4..4f241a3e6f 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 13110137b6..c7cd8fafee 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index 6ff09e4577..7f18bc490b 100644 --- a/spring-data-mongodb-log4j/pom.xml +++ b/spring-data-mongodb-log4j/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 7c2d818c42..e0ce636323 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAMONGO-1158-SNAPSHOT ../pom.xml From d2003c58b136e88c96441eaebc53432b01017ab5 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 9 Feb 2015 15:12:20 +0100 Subject: [PATCH 2/8] DATAMONGO-1158 - Add Support for mongo-java-driver 3.0 We now support mongo-java-driver version 2.x and 3.0 along with MongoDB Server 2.6.7 and 3.0.0. Pleaes note that some of the configurations options might no longer be valid when used with version 3 of the mongo-java-driver. Have a look at the table below so see some of the major differences in using version 2.x or 3.0 | 2.x | 3.0 ----------------------+-----------------------+----------------------------------------------- default WriteConcern | NONE | UNACKNOWLEDGED ----------------------+-----------------------+----------------------------------------------- option for slaveOk | available | ignored ----------------------+-----------------------+----------------------------------------------- option for autoConnect| available | ignored ----------------------+-----------------------+----------------------------------------------- write result checking | available | ignored (errors are exceptions anyway) ----------------------+-----------------------+----------------------------------------------- rest index cache | available | throws UnsupportedOperationException ----------------------+-----------------------+----------------------------------------------- DBRef resolution | via DBRef.fetch | via collection.findOne ----------------------+-----------------------+----------------------------------------------- MapReduce Options | applied | ignored ----------------------+-----------------------+----------------------------------------------- authentication | via UserCredentials | via MongoClient ----------------------+-----------------------+----------------------------------------------- WriteConcernException | not available | translated to DataIntegretyViolationException ----------------------+-----------------------+----------------------------------------------- executeInSession | available | requestStart/requestDone commands ignored. ----------------------+-----------------------+----------------------------------------------- index creation | via createIndex | via createIndex ----------------------+-----------------------+----------------------------------------------- --- .../data/mongodb/MongoClientVersion.java | 47 ++++++ .../ReflectiveDBCollectionInvoker.java | 100 ++++++++++++ .../data/mongodb/ReflectiveDBRefResolver.java | 63 ++++++++ .../data/mongodb/ReflectiveDbInvoker.java | 131 ++++++++++++++++ .../mongodb/ReflectiveMapReduceInvoker.java | 61 ++++++++ .../ReflectiveMongoOptionsInvoker.java | 147 ++++++++++++++++++ .../ReflectiveWriteConcernInvoker.java | 47 ++++++ .../mongodb/ReflectiveWriteResultInvoker.java | 67 ++++++++ .../config/AbstractMongoConfiguration.java | 10 +- .../mongodb/config/MongoParsingUtils.java | 8 +- .../mongodb/core/DefaultIndexOperations.java | 11 +- .../data/mongodb/core/IndexOperations.java | 8 +- .../data/mongodb/core/MongoDbUtils.java | 71 ++++++--- .../core/MongoExceptionTranslator.java | 51 +++--- .../data/mongodb/core/MongoFactoryBean.java | 53 ++++++- .../data/mongodb/core/MongoOperations.java | 19 +++ .../mongodb/core/MongoOptionsFactoryBean.java | 73 +++++---- .../data/mongodb/core/MongoTemplate.java | 85 +++++----- .../mongodb/core/SimpleMongoDbFactory.java | 69 ++++++-- .../mongodb/core/convert/DbRefResolver.java | 13 +- .../core/convert/DefaultDbRefResolver.java | 22 ++- .../core/convert/MappingMongoConverter.java | 6 +- .../mongodb/core/convert/QueryMapper.java | 4 +- .../mongodb/core/index/CompoundIndex.java | 9 +- .../mongodb/core/index/GeoSpatialIndexed.java | 9 +- .../data/mongodb/core/index/Indexed.java | 9 +- .../core/mapreduce/MapReduceOptions.java | 38 +++-- .../core/mapreduce/MapReduceResults.java | 46 +++++- .../data/mongodb/monitor/ServerInfo.java | 33 ++-- .../query/StringBasedMongoQuery.java | 4 +- .../MongoDbFactoryParserIntegrationTests.java | 10 +- .../mongodb/config/MongoNamespaceTests.java | 18 ++- .../data/mongodb/config/MyWriteConcern.java | 1 - ...efaultIndexOperationsIntegrationTests.java | 6 +- .../core/MongoDbUtilsIntegrationTests.java | 14 +- .../MongoExceptionTranslatorUnitTests.java | 25 +-- .../MongoOptionsFactoryBeanUnitTests.java | 10 +- .../data/mongodb/core/MongoTemplateTests.java | 81 +++++----- .../core/SimpleMongoDbFactoryUnitTests.java | 43 ++++- .../core/aggregation/AggregationTests.java | 4 +- .../DbRefMappingMongoConverterUnitTests.java | 37 +++-- .../MappingMongoConverterUnitTests.java | 14 +- .../core/convert/QueryMapperUnitTests.java | 6 +- .../core/convert/UpdateMapperUnitTests.java | 11 +- .../mongodb/core/mapreduce/GroupByTests.java | 7 +- .../mongodb/performance/PerformanceTests.java | 11 +- .../ConvertingParameterAccessorUnitTests.java | 7 +- .../query/MongoQueryCreatorUnitTests.java | 2 +- .../query/StringBasedMongoQueryUnitTests.java | 4 +- src/main/asciidoc/reference/mapping.adoc | 2 +- src/main/asciidoc/reference/mongodb.adoc | 2 +- 51 files changed, 1308 insertions(+), 321 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java new file mode 100644 index 0000000000..70d1af2657 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.util.ClassUtils.*; + +/** + * {@link MongoClientVersion} holds information about the used mongo-java client and is used to distinguish between + * different versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class MongoClientVersion { + + private static final boolean IS_MONGO_30 = isPresent("com.mongodb.binding.SingleServerBinding", + MongoClientVersion.class.getClassLoader()); + private static final boolean IS_ASYNC_CLIENT = isPresent("com.mongodb.async.client.MongoClient", + MongoClientVersion.class.getClassLoader()); + + /** + * @return true if mongo-java-driver version 3 or later is on classpath. + */ + public static boolean isMongo3Driver() { + return IS_MONGO_30; + } + + /** + * @return true if mongodb-driver-async is on classpath. + */ + public static boolean isAsyncClient() { + return IS_ASYNC_CLIENT; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java new file mode 100644 index 0000000000..8454889939 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +/** + * {@link ReflectiveDBCollectionInvoker} provides reflective access to {@link DBCollection} API that is not consistently + * available for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class ReflectiveDBCollectionInvoker { + + private static final Method GEN_INDEX_NAME_METHOD; + private static final Method RESET_INDEX_CHACHE_METHOD; + + static { + + GEN_INDEX_NAME_METHOD = findMethod(DBCollection.class, "genIndexName", DBObject.class); + RESET_INDEX_CHACHE_METHOD = findMethod(DBCollection.class, "resetIndexCache"); + } + + private ReflectiveDBCollectionInvoker() {} + + /** + * Convenience method to generate an index name from the set of fields it is over. Will fall back to a + * mongo-java-driver version 2 compatible way of generating index name in case of + * {@link MongoClientVersion#isMongo3Driver()}. + * + * @param keys the names of the fields used in this index + * @return + */ + public static String generateIndexName(DBObject keys) { + + if (isMongo3Driver()) { + return genIndexName(keys); + } + return (String) invokeMethod(GEN_INDEX_NAME_METHOD, null, keys); + } + + /** + * In case of mongo-java-driver version 2 all indices that have not yet been applied to this collection will be + * cleared. Since this method is not available for the mongo-java-driver version 3 the operation will throw + * {@link UnsupportedOperationException}. + * + * @param dbCollection + * @throws UnsupportedOperationException + */ + public static void resetIndexCache(DBCollection dbCollection) { + + if (isMongo3Driver()) { + throw new UnsupportedOperationException("The mongo java driver 3 does no loger support resetIndexCache!"); + } + + invokeMethod(RESET_INDEX_CHACHE_METHOD, dbCollection); + } + + /** + * Borrowed from mongo-java-driver version 2. See http://github.com/mongodb/mongo-java-driver/blob/r2.13.0/src/main/com/mongodb/DBCollection.java#L754 + * + * @param keys + * @return + */ + private static String genIndexName(DBObject keys) { + + StringBuilder name = new StringBuilder(); + for (String s : keys.keySet()) { + if (name.length() > 0) + name.append('_'); + name.append(s).append('_'); + Object val = keys.get(s); + if (val instanceof Number || val instanceof String) + name.append(val.toString().replace(' ', '_')); + } + return name.toString(); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java new file mode 100644 index 0000000000..8b3515d5e2 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.util.Assert.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.DBRef; + +/** + * {@link ReflectiveDBRefResolver} provides reflective access to {@link DBRef} API that is not consistently available + * for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class ReflectiveDBRefResolver { + + private static final Method FETCH_METHOD; + + static { + + FETCH_METHOD = findMethod(DBRef.class, "fetch"); + } + + /** + * Fetches the object referenced from the database either be directly calling {@link DBRef#fetch()} or + * {@link DBCollection#findOne(Object)}. + * + * @param db can be {@literal null} when using mongo-java-driver version 2. + * @param ref must not be {@literal null}. + * @return the document that this references. + */ + public static DBObject fetch(DB db, DBRef ref) { + + notNull(ref, "DBRef to fetch must not be null!"); + + if (MongoClientVersion.isMongo3Driver()) { + return db.getCollection(ref.getCollectionName()).findOne(ref.getId()); + } + + return (DBObject) invokeMethod(FETCH_METHOD, ref); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java new file mode 100644 index 0000000000..40b7947eaf --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java @@ -0,0 +1,131 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import org.springframework.data.authentication.UserCredentials; + +import com.mongodb.DB; +import com.mongodb.Mongo; + +/** + * {@link ReflectiveDbInvoker} provides reflective access to {@link DB} API that is not consistently available for + * various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public final class ReflectiveDbInvoker { + + private static final Method DB_IS_AUTHENTICATED_METHOD; + private static final Method DB_AUTHENTICATE_METHOD; + private static final Method DB_REQUEST_DONE_METHOD; + private static final Method DB_ADD_USER_METHOD; + private static final Method DB_REQUEST_START_METHOD; + + static { + + DB_IS_AUTHENTICATED_METHOD = findMethod(DB.class, "isAuthenticated"); + DB_AUTHENTICATE_METHOD = findMethod(DB.class, "authenticate", String.class, char[].class); + DB_REQUEST_DONE_METHOD = findMethod(DB.class, "requestDone"); + DB_ADD_USER_METHOD = findMethod(DB.class, "addUser", String.class, char[].class); + DB_REQUEST_START_METHOD = findMethod(DB.class, "requestStart"); + } + + private ReflectiveDbInvoker() {} + + /** + * Authenticate against database using provided credentials in case of a mongo-java-driver version 2. + * + * @param mongo must not be {@literal null}. + * @param db must not be {@literal null}. + * @param credentials must not be {@literal null}. + * @param authenticationDatabaseName + */ + public static void authenticate(Mongo mongo, DB db, UserCredentials credentials, String authenticationDatabaseName) { + + String databaseName = db.getName(); + + DB authDb = databaseName.equals(authenticationDatabaseName) ? db : mongo.getDB(authenticationDatabaseName); + + synchronized (authDb) { + + Boolean isAuthenticated = (Boolean) invokeMethod(DB_IS_AUTHENTICATED_METHOD, authDb); + if (!isAuthenticated) { + + String username = credentials.getUsername(); + String password = credentials.hasPassword() ? credentials.getPassword() : null; + + Boolean authenticated = (Boolean) invokeMethod(DB_AUTHENTICATE_METHOD, authDb, username, + password == null ? null : password.toCharArray()); + if (!authenticated) { + throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], " + + credentials.toString(), databaseName, credentials); + } + } + } + } + + /** + * Starts a new 'consistent request' in case of mongo-java-driver version 2. Will do nothing for mongo-java-driver + * version 3 since the operation is no longer available. + * + * @param db + */ + public static void requestStart(DB db) { + + if (isMongo3Driver()) { + return; + } + + invokeMethod(DB_REQUEST_START_METHOD, db); + } + + /** + * Ends the current 'consistent request'. a new 'consistent request' in case of mongo-java-driver version 2. Will do + * nothing for mongo-java-driver version 3 since the operation is no longer available + * + * @param db + */ + public static void requestDone(DB db) { + + if (isMongo3Driver()) { + return; + } + + invokeMethod(DB_REQUEST_DONE_METHOD, db); + } + + /** + * @param db + * @param username + * @param password + * @throws UnsupportedOperationException + */ + public static void addUser(DB db, String username, char[] password) { + + if (MongoClientVersion.isMongo3Driver()) { + throw new UnsupportedOperationException("Please DB.command to call either the createUser or updateUser command"); + } + + invokeMethod(DB_ADD_USER_METHOD, db, username, password); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java new file mode 100644 index 0000000000..3307bcad4a --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.util.Assert.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import com.mongodb.MapReduceCommand; + +/** + * {@link ReflectiveMapReduceInvoker} provides reflective access to {@link MapReduceCommand} API that is not + * consistently available for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public final class ReflectiveMapReduceInvoker { + + private static final Method ADD_EXTRA_OPTION_METHOD; + + static { + + ADD_EXTRA_OPTION_METHOD = findMethod(MapReduceCommand.class, "addExtraOption", String.class, Object.class); + } + + private ReflectiveMapReduceInvoker() {} + + /** + * Sets the extra option for mongo-java-driver version 2. Will do nothing for mongo-java-driver version 2. + * + * @param cmd can be {@literal null} for mongo-java-driver version 2. + * @param key + * @param value + */ + public static void addExtraOption(MapReduceCommand cmd, String key, Object value) { + + if (isMongo3Driver()) { + return; + } + + notNull(cmd, "MapReduceCommand must not be null!"); + invokeMethod(ADD_EXTRA_OPTION_METHOD, cmd, key, value); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java new file mode 100644 index 0000000000..698b0b38f6 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java @@ -0,0 +1,147 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import org.springframework.beans.DirectFieldAccessor; + +import com.mongodb.MongoOptions; + +/** + * {@link ReflectiveMongoOptionsInvoker} provides reflective access to {@link MongoOptions} API that is not consistently + * available for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class ReflectiveMongoOptionsInvoker { + + private static final Method GET_AUTO_CONNECT_RETRY_METHOD; + private static final Method SET_AUTO_CONNECT_RETRY_METHOD; + private static final Method GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD; + private static final Method SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD; + + static { + + SET_AUTO_CONNECT_RETRY_METHOD = findMethod(MongoOptions.class, "setAutoConnectRetry", boolean.class); + GET_AUTO_CONNECT_RETRY_METHOD = findMethod(MongoOptions.class, "isAutoConnectRetry"); + SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = findMethod(MongoOptions.class, "setMaxAutoConnectRetryTime", long.class); + GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = findMethod(MongoOptions.class, "getMaxAutoConnectRetryTime"); + } + + private ReflectiveMongoOptionsInvoker() {} + + /** + * Sets the retry connection flag for mongo-java-driver version 2. Will do nothing for mongo-java-driver version 3 + * since the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @param autoConnectRetry + */ + public static void setAutoConnectRetry(MongoOptions options, boolean autoConnectRetry) { + + if (isMongo3Driver()) { + return; + } + invokeMethod(SET_AUTO_CONNECT_RETRY_METHOD, options, autoConnectRetry); + } + + /** + * Sets the maxAutoConnectRetryTime attribute for mongo-java-driver version 2. Will do nothing for mongo-java-driver + * version 3 since the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @param maxAutoConnectRetryTime + */ + public static void setMaxAutoConnectRetryTime(MongoOptions options, long maxAutoConnectRetryTime) { + + if (isMongo3Driver()) { + return; + } + invokeMethod(SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options, maxAutoConnectRetryTime); + } + + /** + * Sets the slaveOk attribute for mongo-java-driver version 2. Will do nothing for mongo-java-driver version 3 since + * the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @param slaveOk + */ + public static void setSlaveOk(MongoOptions options, boolean slaveOk) { + + if (isMongo3Driver()) { + return; + } + new DirectFieldAccessor(options).setPropertyValue("slaveOk", slaveOk); + } + + /** + * Gets the slaveOk attribute for mongo-java-driver version 2. Throws {@link UnsupportedOperationException} for + * mongo-java-driver version 3 since the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @return + * @throws UnsupportedOperationException + */ + public static boolean getSlaveOk(MongoOptions options) { + + if (isMongo3Driver()) { + throw new UnsupportedOperationException( + "Cannot get value for autoConnectRetry which has been removed in mongo-java-driver version 3."); + } + return ((Boolean) new DirectFieldAccessor(options).getPropertyValue("slaveOk")).booleanValue(); + } + + /** + * Gets the autoConnectRetry attribute for mongo-java-driver version 2. Throws {@link UnsupportedOperationException} + * for mongo-java-driver version 3 since the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @return + * @throws UnsupportedOperationException + */ + public static boolean getAutoConnectRetry(MongoOptions options) { + + if (isMongo3Driver()) { + throw new UnsupportedOperationException( + "Cannot get value for autoConnectRetry which has been removed in mongo-java-driver version 3."); + } + return ((Boolean) invokeMethod(GET_AUTO_CONNECT_RETRY_METHOD, options)).booleanValue(); + } + + /** + * Gets the maxAutoConnectRetryTime attribute for mongo-java-driver version 2. Throws + * {@link UnsupportedOperationException} for mongo-java-driver version 3 since the method has been removed. + * + * @param options can be {@literal null} for mongo-java-driver version 3. + * @return + * @throws UnsupportedOperationException + */ + public static long getMaxAutoConnectRetryTime(MongoOptions options) { + + if (isMongo3Driver()) { + throw new UnsupportedOperationException( + "Cannot get value for maxAutoConnectRetryTime which has been removed in mongo-java-driver version 3."); + } + + return ((Long) invokeMethod(GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options)).longValue(); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java new file mode 100644 index 0000000000..72965603c6 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; + +import org.springframework.beans.DirectFieldAccessor; + +import com.mongodb.WriteConcern; + +/** + * {@link ReflectiveWriteConcernInvoker} provides reflective access to {@link WriteConcern} API that is not consistently + * available for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class ReflectiveWriteConcernInvoker { + + private static final WriteConcern NONE_OR_UNACKNOWLEDGED; + + static { + + NONE_OR_UNACKNOWLEDGED = isMongo3Driver() ? WriteConcern.UNACKNOWLEDGED : (WriteConcern) new DirectFieldAccessor( + new WriteConcern()).getPropertyValue("NONE"); + } + + /** + * @return {@link WriteConcern#NONE} for mongo-java-driver version 2, otherwise {@link WriteConcern#UNACKNOWLEDGED}. + */ + public static WriteConcern noneOrUnacknowledged() { + return NONE_OR_UNACKNOWLEDGED; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java new file mode 100644 index 0000000000..e9ac6373b0 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb; + +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.util.ReflectionUtils.*; + +import java.lang.reflect.Method; + +import com.mongodb.MongoException; +import com.mongodb.WriteResult; + +/** + * {@link ReflectiveWriteResultInvoker} provides reflective access to {@link WriteResult} API that is not consistently + * available for various driver versions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public final class ReflectiveWriteResultInvoker { + + private static final Method GET_ERROR_METHOD; + private static final Method WAS_ACKNOWLEDGED_METHOD; + + private ReflectiveWriteResultInvoker() {} + + static { + + GET_ERROR_METHOD = findMethod(WriteResult.class, "getError"); + WAS_ACKNOWLEDGED_METHOD = findMethod(WriteResult.class, "wasAcknowledged"); + } + + /** + * @param writeResult can be {@literal null} for mongo-java-driver version 3. + * @return null in case of mongo-java-driver version 3 since errors are thrown as {@link MongoException}. + */ + public static String getError(WriteResult writeResult) { + + if (isMongo3Driver()) { + return null; + } + + return (String) invokeMethod(GET_ERROR_METHOD, writeResult); + } + + /** + * @param writeResult + * @return return in case of mongo-java-driver version 2. + */ + public static boolean wasAcknowledged(WriteResult writeResult) { + return isMongo3Driver() ? ((Boolean) invokeMethod(WAS_ACKNOWLEDGED_METHOD, writeResult)).booleanValue() : true; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java index dc2776d723..b3915c7530 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -46,6 +46,7 @@ import org.springframework.util.StringUtils; import com.mongodb.Mongo; +import com.mongodb.MongoClient; /** * Base class for Spring Data MongoDB configuration using JavaConfig. @@ -54,6 +55,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Ryan Tenney + * @author Christoph Strobl */ @Configuration public abstract class AbstractMongoConfiguration { @@ -70,7 +72,10 @@ public abstract class AbstractMongoConfiguration { * returned by {@link #getDatabaseName()} later on effectively. * * @return + * @deprecated since 1.7. {@link MongoClient} should hold authentication data within + * {@link MongoClient#getCredentialsList()} */ + @Deprecated protected String getAuthenticationDatabaseName() { return null; } @@ -129,7 +134,10 @@ protected String getMappingBasePackage() { * be used. * * @return + * @deprecated since 1.7. {@link MongoClient} should hold authentication data within + * {@link MongoClient#getCredentialsList()} */ + @Deprecated protected UserCredentials getUserCredentials() { return null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index 4ab0b2fc41..b69443012c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2013 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. @@ -33,7 +33,7 @@ * * @author Mark Pollack * @author Oliver Gierke - * @author Thomas Darimont + * @author Thomas Darimont */ abstract class MongoParsingUtils { @@ -80,8 +80,8 @@ static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBui setPropertyValue(optionsDefBuilder, optionsElement, "write-timeout", "writeTimeout"); setPropertyValue(optionsDefBuilder, optionsElement, "write-fsync", "writeFsync"); setPropertyValue(optionsDefBuilder, optionsElement, "slave-ok", "slaveOk"); - setPropertyValue(optionsDefBuilder, optionsElement, "ssl", "ssl"); - setPropertyReference(optionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory"); + setPropertyValue(optionsDefBuilder, optionsElement, "ssl", "ssl"); + setPropertyReference(optionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory"); mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); return true; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java index c971f66129..3f609a16ae 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.dao.DataAccessException; +import org.springframework.data.mongodb.ReflectiveDBCollectionInvoker; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexField; import org.springframework.data.mongodb.core.index.IndexInfo; @@ -73,9 +74,9 @@ public void ensureIndex(final IndexDefinition indexDefinition) { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { DBObject indexOptions = indexDefinition.getIndexOptions(); if (indexOptions != null) { - collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions); + collection.createIndex(indexDefinition.getIndexKeys(), indexOptions); } else { - collection.ensureIndex(indexDefinition.getIndexKeys()); + collection.createIndex(indexDefinition.getIndexKeys()); } return null; } @@ -108,10 +109,12 @@ public void dropAllIndexes() { * (non-Javadoc) * @see org.springframework.data.mongodb.core.IndexOperations#resetIndexCache() */ + @Deprecated public void resetIndexCache() { mongoOperations.execute(collectionName, new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.resetIndexCache(); + + ReflectiveDBCollectionInvoker.resetIndexCache(collection); return null; } }); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java index a4cb7e820b..8362b6196a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2015 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. @@ -20,12 +20,12 @@ import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; - /** * Index operations on a collection. * * @author Mark Pollack * @author Oliver Gierke + * @author Christoph Strobl */ public interface IndexOperations { @@ -51,7 +51,11 @@ public interface IndexOperations { /** * Clears all indices that have not yet been applied to this collection. + * + * @deprecated since 1.7. The mongo-java-driver version 3 does no longer support reseting the index cache. + * @throws {@link UnsupportedOperationException} when used with mongo-java-driver version 3. */ + @Deprecated void resetIndexCache(); /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java index 11a46bda61..6316d507e2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 the original author or authors. + * Copyright 2010-2015 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. @@ -15,15 +15,18 @@ */ package org.springframework.data.mongodb.core; +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.data.mongodb.ReflectiveDbInvoker.*; +import static org.springframework.util.Assert.*; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.authentication.UserCredentials; -import org.springframework.data.mongodb.CannotGetMongoDbConnectionException; import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.util.Assert; import com.mongodb.DB; import com.mongodb.Mongo; +import com.mongodb.MongoClient; /** * Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the @@ -34,6 +37,7 @@ * @author Oliver Gierke * @author Randy Watler * @author Thomas Darimont + * @author Christoph Strobl * @since 1.0 */ public abstract class MongoDbUtils { @@ -65,18 +69,31 @@ public static DB getDB(Mongo mongo, String databaseName) { * @param databaseName the database name, must not be {@literal null} or empty. * @param credentials the credentials to use, must not be {@literal null}. * @return the {@link DB} connection + * @deprecated since 1.7. The {@link MongoClient} itself should hold credentials within + * {@link MongoClient#getCredentialsList()}. */ + @Deprecated public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials) { return getDB(mongo, databaseName, credentials, databaseName); } + /** + * @param mongo + * @param databaseName + * @param credentials + * @param authenticationDatabaseName + * @return + * @deprecated since 1.7. The {@link MongoClient} itself should hold credentials within + * {@link MongoClient#getCredentialsList()}. + */ + @Deprecated public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials, String authenticationDatabaseName) { - Assert.notNull(mongo, "No Mongo instance specified!"); - Assert.hasText(databaseName, "Database name must be given!"); - Assert.notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!"); - Assert.hasText(authenticationDatabaseName, "Authentication database name must not be null or empty!"); + notNull(mongo, "No Mongo instance specified!"); + hasText(databaseName, "Database name must be given!"); + notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!"); + hasText(authenticationDatabaseName, "Authentication database name must not be null or empty!"); return doGetDB(mongo, databaseName, credentials, true, authenticationDatabaseName); } @@ -109,22 +126,9 @@ private static DB doGetDB(Mongo mongo, String databaseName, UserCredentials cred LOGGER.debug("Getting Mongo Database name=[{}]", databaseName); DB db = mongo.getDB(databaseName); - boolean credentialsGiven = credentials.hasUsername() && credentials.hasPassword(); - - DB authDb = databaseName.equals(authenticationDatabaseName) ? db : mongo.getDB(authenticationDatabaseName); - - synchronized (authDb) { - if (credentialsGiven && !authDb.isAuthenticated()) { - - String username = credentials.getUsername(); - String password = credentials.hasPassword() ? credentials.getPassword() : null; - - if (!authDb.authenticate(username, password == null ? null : password.toCharArray())) { - throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], " - + credentials.toString(), databaseName, credentials); - } - } + if (requiresAuthDbAuthentication(credentials)) { + authenticate(mongo, db, credentials, authenticationDatabaseName); } // TX sync active, bind new database to thread @@ -181,16 +185,37 @@ public static boolean isDBTransactional(DB db, Mongo mongo) { * Perform actual closing of the Mongo DB object, catching and logging any cleanup exceptions thrown. * * @param db the DB to close (may be null) + * @deprecated since 1.7. The main use case for this method is to ensure that applications can read their own + * unacknowledged writes, but this is no longer so prevalent since the mongo-java-driver version 3 started + * defaulting to acknowledged writes. */ + @Deprecated public static void closeDB(DB db) { if (db != null) { LOGGER.debug("Closing Mongo DB object"); try { - db.requestDone(); + requestDone(db); } catch (Throwable ex) { LOGGER.debug("Unexpected exception on closing Mongo DB object", ex); } } } + + /** + * Check if credentials present. In case we're using a monog-java-driver version 3 or above we do not have the need + * for authentication as the auth data has to be provied within the MongoClient + * + * @param credentials + * @return + */ + private static boolean requiresAuthDbAuthentication(UserCredentials credentials) { + + if (credentials == null || !credentials.hasUsername()) { + return false; + } + + return isMongo3Driver() ? false : true; + } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java index b8eeb427dd..4bb292967d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 the original author or authors. + * Copyright 2010-2015 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. @@ -15,23 +15,22 @@ */ package org.springframework.data.mongodb.core; +import static java.util.Arrays.*; +import static org.springframework.util.ClassUtils.*; + +import java.util.HashSet; +import java.util.Set; + import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.UncategorizedMongoDbException; -import com.mongodb.MongoCursorNotFoundException; import com.mongodb.MongoException; -import com.mongodb.MongoException.CursorNotFound; -import com.mongodb.MongoException.DuplicateKey; -import com.mongodb.MongoException.Network; -import com.mongodb.MongoInternalException; -import com.mongodb.MongoServerSelectionException; -import com.mongodb.MongoSocketException; -import com.mongodb.MongoTimeoutException; /** * Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate @@ -40,9 +39,21 @@ * * @author Oliver Gierke * @author Michal Vich + * @author Christoph Strobl */ public class MongoExceptionTranslator implements PersistenceExceptionTranslator { + private static final Set DULICATE_KEY_EXCEPTIONS = new HashSet(asList("MongoException.DuplicateKey", + "DuplicateKeyException")); + + private static final Set RESOURCE_FAILURE_EXCEPTIONS = new HashSet(asList("MongoException.Network", + "MongoSocketException", "MongoException.CursorNotFound", "MongoCursorNotFoundException", + "MongoServerSelectionException", "MongoTimeoutException")); + + private static final Set RESOURCE_USAGE_EXCEPTIONS = new HashSet(asList("MongoInternalException")); + + private static final Set DATA_INTEGRETY_EXCEPTIONS = new HashSet(asList("WriteConcernException")); + /* * (non-Javadoc) * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) @@ -51,28 +62,22 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { // Check for well-known MongoException subclasses. - if (ex instanceof DuplicateKey || ex instanceof DuplicateKeyException) { - return new DuplicateKeyException(ex.getMessage(), ex); - } - - if (ex instanceof Network || ex instanceof MongoSocketException) { - return new DataAccessResourceFailureException(ex.getMessage(), ex); - } + String exception = getShortName(getUserClass(ex.getClass())); - if (ex instanceof CursorNotFound || ex instanceof MongoCursorNotFoundException) { - return new DataAccessResourceFailureException(ex.getMessage(), ex); + if (DULICATE_KEY_EXCEPTIONS.contains(exception)) { + return new DuplicateKeyException(ex.getMessage(), ex); } - if (ex instanceof MongoServerSelectionException) { + if (RESOURCE_FAILURE_EXCEPTIONS.contains(exception)) { return new DataAccessResourceFailureException(ex.getMessage(), ex); } - if (ex instanceof MongoTimeoutException) { - return new DataAccessResourceFailureException(ex.getMessage(), ex); + if (RESOURCE_USAGE_EXCEPTIONS.contains(exception)) { + return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex); } - if (ex instanceof MongoInternalException) { - return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex); + if (DATA_INTEGRETY_EXCEPTIONS.contains(exception)) { + return new DataIntegrityViolationException(ex.getMessage(), ex); } // All other MongoExceptions diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java index a7c156144f..41bbc9231a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 the original author or authors. + * Copyright 2010-2015 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. @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,6 +30,9 @@ import org.springframework.util.StringUtils; import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoCredential; import com.mongodb.MongoOptions; import com.mongodb.ServerAddress; import com.mongodb.WriteConcern; @@ -40,6 +44,7 @@ * @author Graeme Rocher * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl * @since 1.0 */ public class MongoFactoryBean implements FactoryBean, InitializingBean, DisposableBean, @@ -48,25 +53,52 @@ public class MongoFactoryBean implements FactoryBean, InitializingBean, D private Mongo mongo; private MongoOptions mongoOptions; + private MongoClientOptions mongoClientOptions; + private String host; private Integer port; private WriteConcern writeConcern; private List replicaSetSeeds; private List replicaPair; + private List credentials; private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); + /** + * @param mongoOptions + * @deprecated since 1.7. Please use {@link #setMongoClientOptions(MongoClientOptions)} + */ + @Deprecated public void setMongoOptions(MongoOptions mongoOptions) { this.mongoOptions = mongoOptions; } + /** + * Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}. + * + * @param mongoClientOptions + * @since 1.7 + */ + public void setMongoClientOptions(MongoClientOptions mongoClientOptions) { + this.mongoClientOptions = mongoClientOptions; + } + + /** + * Set the list of credentials to be used when creating {@link MongoClient}. + * + * @param credentials can be {@literal null}. + * @since 1.7 + */ + public void setCredentials(List credentials) { + this.credentials = credentials; + } + public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) { this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds); } /** * @deprecated use {@link #setReplicaSetSeeds(ServerAddress[])} instead - * * @param replicaPair */ @Deprecated @@ -148,8 +180,21 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { * (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ - @SuppressWarnings("deprecation") public void afterPropertiesSet() throws Exception { + this.mongo = mongoClientOptions != null ? createMongoClient() : createMongo(); + } + + private MongoClient createMongoClient() { + + if (!isNullOrEmpty(replicaPair)) { + throw new CannotGetMongoDbConnectionException( + "Cannot create MongoClient with replicaPair please use replicaSetSeeds."); + } + return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions); + } + + @SuppressWarnings("deprecation") + private Mongo createMongo() throws UnknownHostException { Mongo mongo; ServerAddress defaultOptions = new ServerAddress(); @@ -175,7 +220,7 @@ public void afterPropertiesSet() throws Exception { mongo.setWriteConcern(writeConcern); } - this.mongo = mongo; + return mongo; } private boolean isNullOrEmpty(Collection elements) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index e38912bfe9..24748a86b8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -35,8 +35,10 @@ import org.springframework.data.mongodb.core.query.Update; import com.mongodb.CommandResult; +import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBObject; +import com.mongodb.ReadPreference; import com.mongodb.WriteResult; /** @@ -85,9 +87,23 @@ public interface MongoOperations { * * @param command a MongoDB command * @param options query options to use + * @deprecated since 1.7. Please use {@link #executeCommand(DBObject, ReadPreference)}, as the mongo-java-driver + * version 3 no longer supports this operation. */ + @Deprecated CommandResult executeCommand(DBObject command, int options); + /** + * Execute a MongoDB command. Any errors that result from executing this command will be converted into Spring's DAO + * exception hierarchy. + * + * @param command a MongoDB command. + * @param readPreference read preferences to use. + * @return + * @since 1.7 + */ + CommandResult executeCommand(DBObject command, ReadPreference readPreference); + /** * Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler. * @@ -143,7 +159,10 @@ public interface MongoOperations { * @param return type * @param action callback that specified the MongoDB actions to perform on the DB instance * @return a result object returned by the action or null + * @deprecated since 1.7 as the mongo-java-driver version 3 does not longer support request boundaries via + * {@link DB#requestStart()} and {@link DB#requestDone()}. */ + @Deprecated T executeInSession(DbCallback action); /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java index 63a1075748..79ad28a0a8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 the original author or authors. + * Copyright 2010-2015 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. @@ -15,38 +15,47 @@ */ package org.springframework.data.mongodb.core; +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; + import javax.net.ssl.SSLSocketFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker; import com.mongodb.MongoOptions; /** - * A factory bean for construction of a {@link MongoOptions} instance. + * A factory bean for construction of a {@link MongoOptions} instance. In case used with mongo-java-driver version 3 + * porperties not suppprted by the driver will be ignored. * * @author Graeme Rocher * @author Mark Pollack * @author Mike Saavedra * @author Thomas Darimont + * @author Christoph Strobl */ @SuppressWarnings("deprecation") public class MongoOptionsFactoryBean implements FactoryBean, InitializingBean { private static final MongoOptions DEFAULT_MONGO_OPTIONS = new MongoOptions(); - private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.connectionsPerHost; - private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier; - private int maxWaitTime = DEFAULT_MONGO_OPTIONS.maxWaitTime; - private int connectTimeout = DEFAULT_MONGO_OPTIONS.connectTimeout; - private int socketTimeout = DEFAULT_MONGO_OPTIONS.socketTimeout; - private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.socketKeepAlive; - private boolean autoConnectRetry = DEFAULT_MONGO_OPTIONS.autoConnectRetry; - private long maxAutoConnectRetryTime = DEFAULT_MONGO_OPTIONS.maxAutoConnectRetryTime; - private int writeNumber = DEFAULT_MONGO_OPTIONS.w; - private int writeTimeout = DEFAULT_MONGO_OPTIONS.wtimeout; - private boolean writeFsync = DEFAULT_MONGO_OPTIONS.fsync; - private boolean slaveOk = DEFAULT_MONGO_OPTIONS.slaveOk; + private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost(); + private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS + .getThreadsAllowedToBlockForConnectionMultiplier(); + private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime(); + private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout(); + private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout(); + private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive(); + private int writeNumber = DEFAULT_MONGO_OPTIONS.getW(); + private int writeTimeout = DEFAULT_MONGO_OPTIONS.getWtimeout(); + private boolean writeFsync = DEFAULT_MONGO_OPTIONS.isFsync(); + + private boolean autoConnectRetry = !isMongo3Driver() ? getAutoConnectRetry(DEFAULT_MONGO_OPTIONS) : false; + private long maxAutoConnectRetryTime = !isMongo3Driver() ? getMaxAutoConnectRetryTime(DEFAULT_MONGO_OPTIONS) : -1; + private boolean slaveOk = !isMongo3Driver() ? getSlaveOk(DEFAULT_MONGO_OPTIONS) : false; + private boolean ssl; private SSLSocketFactory sslSocketFactory; @@ -144,7 +153,10 @@ public void setWriteFsync(boolean writeFsync) { /** * Configures whether or not the system retries automatically on a failed connect. This defaults to {@literal false}. + * + * @deprecated since 1.7. */ + @Deprecated public void setAutoConnectRetry(boolean autoConnectRetry) { this.autoConnectRetry = autoConnectRetry; } @@ -154,7 +166,9 @@ public void setAutoConnectRetry(boolean autoConnectRetry) { * defaults to {@literal 0}, which means to use the default {@literal 15s} if {@link #autoConnectRetry} is on. * * @param maxAutoConnectRetryTime the maxAutoConnectRetryTime to set + * @deprecated since 1.7 */ + @Deprecated public void setMaxAutoConnectRetryTime(long maxAutoConnectRetryTime) { this.maxAutoConnectRetryTime = maxAutoConnectRetryTime; } @@ -163,7 +177,9 @@ public void setMaxAutoConnectRetryTime(long maxAutoConnectRetryTime) { * Specifies if the driver is allowed to read from secondaries or slaves. Defaults to {@literal false}. * * @param slaveOk true if the driver should read from secondaries or slaves. + * @deprecated since 1.7 */ + @Deprecated public void setSlaveOk(boolean slaveOk) { this.slaveOk = slaveOk; } @@ -202,23 +218,28 @@ public void afterPropertiesSet() { MongoOptions options = new MongoOptions(); - options.connectionsPerHost = connectionsPerHost; - options.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier; - options.maxWaitTime = maxWaitTime; - options.connectTimeout = connectTimeout; - options.socketTimeout = socketTimeout; - options.socketKeepAlive = socketKeepAlive; - options.autoConnectRetry = autoConnectRetry; - options.maxAutoConnectRetryTime = maxAutoConnectRetryTime; - options.slaveOk = slaveOk; - options.w = writeNumber; - options.wtimeout = writeTimeout; - options.fsync = writeFsync; + options.setConnectionsPerHost(connectionsPerHost); + options.setThreadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier); + options.setMaxWaitTime(maxWaitTime); + options.setConnectTimeout(connectTimeout); + options.setSocketTimeout(socketTimeout); + options.setSocketKeepAlive(socketKeepAlive); + + options.setW(writeNumber); + options.setWtimeout(writeTimeout); + options.setFsync(writeFsync); if (ssl) { options.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault()); } + if (!isMongo3Driver()) { + + ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry); + ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime); + ReflectiveMongoOptionsInvoker.setSlaveOk(options, slaveOk); + } + this.options = options; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index f8f391c3da..53cf65b8b9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 the original author or authors. + * Copyright 2010-2015 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. @@ -15,6 +15,10 @@ */ package org.springframework.data.mongodb.core; +import static com.mongodb.ReadPreference.*; +import static org.springframework.data.mongodb.ReflectiveDbInvoker.*; +import static org.springframework.data.mongodb.ReflectiveMapReduceInvoker.*; +import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.SerializationUtils.*; @@ -102,6 +106,7 @@ import org.springframework.util.StringUtils; import com.mongodb.BasicDBObject; +import com.mongodb.Bytes; import com.mongodb.CommandResult; import com.mongodb.DB; import com.mongodb.DBCollection; @@ -332,11 +337,24 @@ public CommandResult doInDB(DB db) throws MongoException, DataAccessException { return result; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.MongoOperations#executeCommand(com.mongodb.DBObject, int) + */ + @Deprecated public CommandResult executeCommand(final DBObject command, final int options) { + return executeCommand(command, (options & Bytes.QUERYOPTION_SLAVEOK) != 0 ? secondaryPreferred() : primary()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.MongoOperations#executeCommand(com.mongodb.DBObject, com.mongodb.ReadPreference) + */ + public CommandResult executeCommand(final DBObject command, final ReadPreference readPreference) { CommandResult result = execute(new DbCallback() { public CommandResult doInDB(DB db) throws MongoException, DataAccessException { - return db.command(command, options); + return db.command(command, readPreference); } }); @@ -414,14 +432,20 @@ public T execute(String collectionName, CollectionCallback callback) { } } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.MongoOperations#executeInSession(org.springframework.data.mongodb.core.DbCallback) + */ + @Deprecated public T executeInSession(final DbCallback action) { + return execute(new DbCallback() { public T doInDB(DB db) throws MongoException, DataAccessException { try { - db.requestStart(); + requestStart(db); return action.doInDB(db); } finally { - db.requestDone(); + requestDone(db); } } }); @@ -1026,7 +1050,8 @@ public WriteResult doInCollection(DBCollection collection) throws MongoException : collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse); if (entity != null && entity.hasVersionProperty() && !multi) { - if (writeResult.getN() == 0 && dbObjectContainsVersionProperty(queryObj, entity)) { + if (wasAcknowledged(writeResult) && writeResult.getN() == 0 + && dbObjectContainsVersionProperty(queryObj, entity)) { throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity: " + updateObj.toMap().toString() + " to collection " + collectionName); } @@ -1242,25 +1267,24 @@ public MapReduceResults mapReduce(Query query, String inputCollectionName String mapFunc = replaceWithResourceIfNecessary(mapFunction); String reduceFunc = replaceWithResourceIfNecessary(reduceFunction); DBCollection inputCollection = getCollection(inputCollectionName); + MapReduceCommand command = new MapReduceCommand(inputCollection, mapFunc, reduceFunc, - mapReduceOptions.getOutputCollection(), mapReduceOptions.getOutputType(), null); + mapReduceOptions.getOutputCollection(), mapReduceOptions.getOutputType(), query == null + || query.getQueryObject() == null ? null : queryMapper.getMappedObject(query.getQueryObject(), null)); - DBObject commandObject = copyQuery(query, copyMapReduceOptions(mapReduceOptions, command)); + copyMapReduceOptionsToCommand(query, mapReduceOptions, command); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Executing MapReduce on collection [" + command.getInput() + "], mapFunction [" + mapFunc + "], reduceFunction [" + reduceFunc + "]"); } - CommandResult commandResult = command.getOutputType() == MapReduceCommand.OutputType.INLINE ? executeCommand( - commandObject, getDb().getOptions()) : executeCommand(commandObject); - handleCommandError(commandResult, commandObject); + MapReduceOutput mapReduceOutput = inputCollection.mapReduce(command); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MapReduce command result = [{}]", serializeToJsonSafely(commandObject)); + LOGGER.debug("MapReduce command result = [{}]", serializeToJsonSafely(mapReduceOutput.results())); } - MapReduceOutput mapReduceOutput = new MapReduceOutput(inputCollection, commandObject, commandResult); List mappedResults = new ArrayList(); DbObjectCallback callback = new ReadDbObjectCallback(mongoConverter, entityClass); @@ -1268,7 +1292,7 @@ public MapReduceResults mapReduce(Query query, String inputCollectionName mappedResults.add(callback.doWith(dbObject)); } - return new MapReduceResults(mappedResults, commandResult); + return new MapReduceResults(mappedResults, mapReduceOutput); } public GroupByResults group(String inputCollectionName, GroupBy groupBy, Class entityClass) { @@ -1475,51 +1499,40 @@ protected String replaceWithResourceIfNecessary(String function) { return func; } - private DBObject copyQuery(Query query, DBObject copyMapReduceOptions) { + private void copyMapReduceOptionsToCommand(Query query, MapReduceOptions mapReduceOptions, + MapReduceCommand mapReduceCommand) { + if (query != null) { if (query.getSkip() != 0 || query.getFieldsObject() != null) { throw new InvalidDataAccessApiUsageException( "Can not use skip or field specification with map reduce operations"); } - if (query.getQueryObject() != null) { - copyMapReduceOptions.put("query", queryMapper.getMappedObject(query.getQueryObject(), null)); - } + if (query.getLimit() > 0) { - copyMapReduceOptions.put("limit", query.getLimit()); + mapReduceCommand.setLimit(query.getLimit()); } if (query.getSortObject() != null) { - copyMapReduceOptions.put("sort", queryMapper.getMappedObject(query.getSortObject(), null)); + mapReduceCommand.setSort(queryMapper.getMappedObject(query.getSortObject(), null)); } } - return copyMapReduceOptions; - } - private DBObject copyMapReduceOptions(MapReduceOptions mapReduceOptions, MapReduceCommand command) { if (mapReduceOptions.getJavaScriptMode() != null) { - command.addExtraOption("jsMode", true); + mapReduceCommand.setJsMode(true); } if (!mapReduceOptions.getExtraOptions().isEmpty()) { for (Map.Entry entry : mapReduceOptions.getExtraOptions().entrySet()) { - command.addExtraOption(entry.getKey(), entry.getValue()); + addExtraOption(mapReduceCommand, entry.getKey(), entry.getValue()); } } if (mapReduceOptions.getFinalizeFunction() != null) { - command.setFinalize(this.replaceWithResourceIfNecessary(mapReduceOptions.getFinalizeFunction())); + mapReduceCommand.setFinalize(this.replaceWithResourceIfNecessary(mapReduceOptions.getFinalizeFunction())); } if (mapReduceOptions.getOutputDatabase() != null) { - command.setOutputDB(mapReduceOptions.getOutputDatabase()); + mapReduceCommand.setOutputDB(mapReduceOptions.getOutputDatabase()); } if (!mapReduceOptions.getScopeVariables().isEmpty()) { - command.setScope(mapReduceOptions.getScopeVariables()); - } - - DBObject commandObject = command.toDBObject(); - DBObject outObject = (DBObject) commandObject.get("out"); - - if (mapReduceOptions.getOutputSharded() != null) { - outObject.put("sharded", mapReduceOptions.getOutputSharded()); + mapReduceCommand.setScope(mapReduceOptions.getScopeVariables()); } - return commandObject; } public Set getCollectionNames() { @@ -1900,7 +1913,7 @@ protected void handleAnyWriteResultErrors(WriteResult writeResult, DBObject quer return; } - String error = writeResult.getError(); + String error = getError(writeResult); if (error == null) { return; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java index 0a9d8e4b78..8772f853ac 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core; +import static org.springframework.util.Assert.*; + import java.net.UnknownHostException; import org.springframework.beans.factory.DisposableBean; @@ -22,11 +24,12 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; import com.mongodb.DB; import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientURI; import com.mongodb.MongoException; import com.mongodb.MongoURI; import com.mongodb.WriteConcern; @@ -37,6 +40,7 @@ * @author Mark Pollack * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory { @@ -54,7 +58,9 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory { * * @param mongo Mongo instance, must not be {@literal null}. * @param databaseName database name, not be {@literal null} or empty. + * @deprecated since 1.7. Please use {@link #SimpleMongoDbFactory(MongoClient, String)}. */ + @Deprecated public SimpleMongoDbFactory(Mongo mongo, String databaseName) { this(mongo, databaseName, null); } @@ -65,7 +71,9 @@ public SimpleMongoDbFactory(Mongo mongo, String databaseName) { * @param mongo Mongo instance, must not be {@literal null}. * @param databaseName Database name, must not be {@literal null} or empty. * @param credentials username and password. + * @deprecated since 1.7. The credentials used should be provided by {@link MongoClient#getCredentialsList()}. */ + @Deprecated public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials) { this(mongo, databaseName, credentials, false, null); } @@ -77,7 +85,9 @@ public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials cr * @param databaseName Database name, must not be {@literal null} or empty. * @param credentials username and password. * @param authenticationDatabaseName the database name to use for authentication + * @deprecated since 1.7. The credentials used should be provided by {@link MongoClient#getCredentialsList()}. */ + @Deprecated public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials, String authenticationDatabaseName) { this(mongo, databaseName, credentials, false, authenticationDatabaseName); @@ -90,20 +100,42 @@ public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials cr * @throws MongoException * @throws UnknownHostException * @see MongoURI + * @deprecated since 1.7. Please use {@link #SimpleMongoDbFactory(MongoClientURI)} instead. */ - @SuppressWarnings("deprecation") + @Deprecated public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException { this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())), true, uri.getDatabase()); } + /** + * Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}. + * + * @param uri must not be {@literal null}. + * @throws UnknownHostException + * @since 1.7 + */ + public SimpleMongoDbFactory(MongoClientURI uri) throws UnknownHostException { + this(new MongoClient(uri), uri.getDatabase(), true); + } + + /** + * Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}. + * + * @param mongoClient must not be {@literal null}. + * @param databaseName must not be {@literal null}. + * @since 1.7 + */ + public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) { + this(mongoClient, databaseName, false); + } + private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials, boolean mongoInstanceCreated, String authenticationDatabaseName) { - Assert.notNull(mongo, "Mongo must not be null"); - Assert.hasText(databaseName, "Database name must not be empty"); - Assert.isTrue(databaseName.matches("[\\w-]+"), - "Database name must only contain letters, numbers, underscores and dashes!"); + notNull(mongo, "Mongo must not be null"); + hasText(databaseName, "Database name must not be empty"); + isTrue(databaseName.matches("[\\w-]+"), "Database name must only contain letters, numbers, underscores and dashes!"); this.mongo = mongo; this.databaseName = databaseName; @@ -113,10 +145,29 @@ private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials c this.authenticationDatabaseName = StringUtils.hasText(authenticationDatabaseName) ? authenticationDatabaseName : databaseName; - Assert.isTrue(this.authenticationDatabaseName.matches("[\\w-]+"), + isTrue(this.authenticationDatabaseName.matches("[\\w-]+"), "Authentication database name must only contain letters, numbers, underscores and dashes!"); } + /** + * @param client + * @param databaseName + * @param mongoInstanceCreated + * @since 1.7 + */ + private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) { + + notNull(client, "MongoClient must not be null!"); + hasText(databaseName, "Database name must not be empty!"); + + this.mongo = client; + this.databaseName = databaseName; + this.mongoInstanceCreated = mongoInstanceCreated; + this.exceptionTranslator = new MongoExceptionTranslator(); + this.credentials = UserCredentials.NO_CREDENTIALS; + this.authenticationDatabaseName = databaseName; + } + /** * Configures the {@link WriteConcern} to be used on the {@link DB} instance being created. * @@ -140,7 +191,7 @@ public DB getDb() throws DataAccessException { */ public DB getDb(String dbName) throws DataAccessException { - Assert.hasText(dbName, "Database name must not be empty."); + hasText(dbName, "Database name must not be empty."); DB db = MongoDbUtils.getDB(mongo, dbName, credentials, authenticationDatabaseName); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java index 47910299b5..a43b742c6d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -18,6 +18,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import com.mongodb.DBObject; import com.mongodb.DBRef; /** @@ -25,6 +26,7 @@ * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl * @since 1.4 */ public interface DbRefResolver { @@ -53,4 +55,13 @@ Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolver */ DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, MongoPersistentEntity entity, Object id); + + /** + * Actually loads the {@link DBRef} from the datasource. + * + * @param dbRef must not be {@literal null}. + * @return + * @since 1.7 + */ + DBObject fetch(DBRef dbRef); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java index 8b64c7cb97..941795df9d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -34,14 +34,14 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.LazyLoadingException; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.ReflectiveDBRefResolver; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.objenesis.ObjenesisStd; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; -import com.mongodb.DB; +import com.mongodb.DBObject; import com.mongodb.DBRef; /** @@ -50,6 +50,7 @@ * * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl * @since 1.4 */ public class DefaultDbRefResolver implements DbRefResolver { @@ -97,11 +98,16 @@ public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefR @Override public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, MongoPersistentEntity entity, Object id) { + return new DBRef(entity.getCollection(), id); + } - DB db = mongoDbFactory.getDb(); - db = annotation != null && StringUtils.hasText(annotation.db()) ? mongoDbFactory.getDb(annotation.db()) : db; - - return new DBRef(db, entity.getCollection(), id); + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.DbRefResolver#fetch(com.mongodb.DBRef) + */ + @Override + public DBObject fetch(DBRef dbRef) { + return ReflectiveDBRefResolver.fetch(mongoDbFactory.getDb(), dbRef); } /** @@ -282,7 +288,7 @@ private String proxyToString(Object proxy) { StringBuilder description = new StringBuilder(); if (dbref != null) { - description.append(dbref.getRef()); + description.append(dbref.getCollectionName()); description.append(":"); description.append(dbref.getId()); } else { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index d3aa3ba6f4..94c8bada45 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 by the original author(s). + * Copyright 2011-2015 by the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1176,7 +1176,7 @@ private T potentiallyReadOrResolveDbRef(DBRef dbref, TypeInformation type return (T) dbref; } - Object object = dbref == null ? null : path.getPathItem(dbref.getId(), dbref.getRef()); + Object object = dbref == null ? null : path.getPathItem(dbref.getId(), dbref.getCollectionName()); if (object != null) { return (T) object; @@ -1192,6 +1192,6 @@ private T potentiallyReadOrResolveDbRef(DBRef dbref, TypeInformation type * @return */ DBObject readRef(DBRef ref) { - return ref.fetch(); + return dbRefResolver.fetch(ref); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index 661f6940bf..82a97e2d5b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -390,7 +390,7 @@ protected Object convertAssociation(Object source, MongoPersistentProperty prope if (source instanceof DBRef) { DBRef ref = (DBRef) source; - return new DBRef(ref.getDB(), ref.getRef(), convertId(ref.getId())); + return new DBRef(ref.getCollectionName(), convertId(ref.getId())); } if (source instanceof Iterable) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java index 2b1807607b..9e5c4a088a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -28,6 +28,7 @@ * @author Oliver Gierke * @author Philipp Schneider * @author Johno Crawford + * @author Christoph Strobl */ @Target({ ElementType.TYPE }) @Documented @@ -105,9 +106,9 @@ * *
 	 * 
-	 * db.root.ensureIndex( { hybrid.h1: 1, hybrid.h2: 1 } , { name: "hybrid.compound_index" } )
-	 * db.root.ensureIndex( { nested.n1: 1, nested.n2: 1 } , { name: "nested.compound_index" } )
-	 * db.hybrid.ensureIndex( { h1: 1, h2: 1 } , { name: "compound_index" } )
+	 * db.root.createIndex( { hybrid.h1: 1, hybrid.h2: 1 } , { name: "hybrid.compound_index" } )
+	 * db.root.createIndex( { nested.n1: 1, nested.n2: 1 } , { name: "nested.compound_index" } )
+	 * db.hybrid.createIndex( { h1: 1, h2: 1 } , { name: "compound_index" } )
 	 * 
 	 * 
* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java index 0f13634ff1..119066877e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeoSpatialIndexed.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 the original author or authors. + * Copyright 2010-2015 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. @@ -26,6 +26,7 @@ * @author Jon Brisbin * @author Laurent Canet * @author Thomas Darimont + * @author Christoph Strobl */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @@ -62,9 +63,9 @@ * *
 	 * 
-	 * db.root.ensureIndex( { hybrid.h1: "2d" } , { name: "hybrid.index" } )
-	 * db.root.ensureIndex( { nested.n1: "2d" } , { name: "nested.index" } )
-	 * db.hybrid.ensureIndex( { h1: "2d" } , { name: "index" } )
+	 * db.root.createIndex( { hybrid.h1: "2d" } , { name: "hybrid.index" } )
+	 * db.root.createIndex( { nested.n1: "2d" } , { name: "nested.index" } )
+	 * db.hybrid.createIndex( { h1: "2d" } , { name: "index" } )
 	 * 
 	 * 
* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java index 58f7c65b62..31eb1ef177 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -28,6 +28,7 @@ * @author Philipp Schneider * @author Johno Crawford * @author Thomas Darimont + * @author Christoph Strobl */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @@ -88,9 +89,9 @@ * *
 	 * 
-	 * db.root.ensureIndex( { hybrid.h1: 1 } , { name: "hybrid.index" } )
-	 * db.root.ensureIndex( { nested.n1: 1 } , { name: "nested.index" } )
-	 * db.hybrid.ensureIndex( { h1: 1} , { name: "index" } )
+	 * db.root.createIndex( { hybrid.h1: 1 } , { name: "hybrid.index" } )
+	 * db.root.createIndex( { nested.n1: 1 } , { name: "nested.index" } )
+	 * db.hybrid.createIndex( { h1: 1} , { name: "index" } )
 	 * 
 	 * 
* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java index 54b96cc093..41b71b2398 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * Copyright 2010-2015 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. @@ -22,6 +22,11 @@ import com.mongodb.DBObject; import com.mongodb.MapReduceCommand; +/** + * @author Mark Pollack + * @author Oliver Gierke + * @author Christoph Strobl + */ public class MapReduceOptions { private String outputCollection; @@ -197,12 +202,19 @@ public MapReduceOptions verbose(boolean verbose) { * @param key The key option * @param value The value of the option * @return MapReduceOptions so that methods can be chained in a fluent API style + * @deprecated since 1.7. */ + @Deprecated public MapReduceOptions extraOption(String key, Object value) { extraOptions.put(key, value); return this; } + /** + * @return + * @deprecated since 1.7 + */ + @Deprecated public Map getExtraOptions() { return extraOptions; } @@ -263,18 +275,18 @@ protected BasicDBObject createOutObject() { BasicDBObject out = new BasicDBObject(); switch (outputType) { - case INLINE: - out.put("inline", 1); - break; - case REPLACE: - out.put("replace", outputCollection); - break; - case MERGE: - out.put("merge", outputCollection); - break; - case REDUCE: - out.put("reduce", outputCollection); - break; + case INLINE: + out.put("inline", 1); + break; + case REPLACE: + out.put("replace", outputCollection); + break; + case MERGE: + out.put("merge", outputCollection); + break; + case REDUCE: + out.put("reduce", outputCollection); + break; } if (outputDatabase != null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java index 981ca1a3ff..f2ad71bc75 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-2015 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. @@ -15,18 +15,20 @@ */ package org.springframework.data.mongodb.core.mapreduce; +import static org.springframework.util.Assert.*; + import java.util.Iterator; import java.util.List; -import org.springframework.util.Assert; - import com.mongodb.DBObject; +import com.mongodb.MapReduceOutput; /** * Collects the results of performing a MapReduce operations. * * @author Mark Pollack * @author Oliver Gierke + * @author Christoph Strobl * @param The class in which the results are mapped onto, accessible via an iterator. */ public class MapReduceResults implements Iterable { @@ -42,11 +44,13 @@ public class MapReduceResults implements Iterable { * * @param mappedResults must not be {@literal null}. * @param rawResults must not be {@literal null}. + * @deprecated since 1.7. Please use {@link #MapReduceResults(List, MapReduceOutput)} */ + @Deprecated public MapReduceResults(List mappedResults, DBObject rawResults) { - Assert.notNull(mappedResults); - Assert.notNull(rawResults); + notNull(mappedResults); + notNull(rawResults); this.mappedResults = mappedResults; this.rawResults = rawResults; @@ -55,6 +59,25 @@ public MapReduceResults(List mappedResults, DBObject rawResults) { this.outputCollection = parseOutputCollection(rawResults); } + /** + * Creates a new {@link MapReduceResults} from the given mapped results and the {@link MapReduceOutput}. + * + * @param mappedResults must not be {@literal null}. + * @param mapReduceOutput must not be {@literal null}. + * @since 1.7 + */ + public MapReduceResults(List mappedResults, MapReduceOutput mapReduceOutput) { + + notNull(mappedResults, "MappedResults must not be null!"); + notNull(mapReduceOutput, "MapReduceOutput must not be null!"); + + this.mappedResults = mappedResults; + this.rawResults = null; + this.mapReduceTiming = parseTiming(mapReduceOutput); + this.mapReduceCounts = parseCounts(mapReduceOutput); + this.outputCollection = parseOutputCollection(mapReduceOutput); + } + /* * (non-Javadoc) * @see java.lang.Iterable#iterator() @@ -145,4 +168,17 @@ private String parseOutputCollection(DBObject rawResults) { return resultField instanceof DBObject ? ((DBObject) resultField).get("collection").toString() : resultField .toString(); } + + private MapReduceCounts parseCounts(final MapReduceOutput mapReduceOutput) { + return new MapReduceCounts(mapReduceOutput.getInputCount(), mapReduceOutput.getEmitCount(), + mapReduceOutput.getOutputCount()); + } + + private String parseOutputCollection(final MapReduceOutput mapReduceOutput) { + return mapReduceOutput.getCollectionName(); + } + + private MapReduceTiming parseTiming(MapReduceOutput mapReduceOutput) { + return new MapReduceTiming(-1, -1, mapReduceOutput.getDuration()); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java index de2bb64dd9..28f9383cde 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2015 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. @@ -22,13 +22,14 @@ import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.jmx.support.MetricType; -import com.mongodb.Mongo; - +import com.mongodb.Mongo; + /** * Expose basic server information via JMX * * @author Mark Pollack - * @author Thomas Darimont + * @author Thomas Darimont + * @author Christoph Strobl */ @ManagedResource(description = "Server Information") public class ServerInfo extends AbstractMonitor { @@ -37,20 +38,20 @@ public ServerInfo(Mongo mongo) { this.mongo = mongo; } - /** - * Returns the hostname of the used server reported by mongo. - * - * @return the reported hostname can also be an IP address. - * @throws UnknownHostException - */ + /** + * Returns the hostname of the used server reported by mongo. + * + * @return the reported hostname can also be an IP address. + * @throws UnknownHostException + */ @ManagedOperation(description = "Server host name") public String getHostName() throws UnknownHostException { - - /* - * UnknownHostException is not necessary anymore, but clients could have - * called this method in a try..catch(UnknownHostException) already - */ - return getServerStatus().getServerUsed().getHost(); + + /* + * UnknownHostException is not necessary anymore, but clients could have + * called this method in a try..catch(UnknownHostException) already + */ + return mongo.getAddress().getHost(); } @ManagedMetric(displayName = "Uptime Estimate") diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java index 5059f987e7..b088d22a19 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -254,7 +254,7 @@ private void collectParameterReferencesIntoBindings(List bindi DBRef dbref = (DBRef) value; - potentiallyAddBinding(dbref.getRef(), bindings); + potentiallyAddBinding(dbref.getCollectionName(), bindings); potentiallyAddBinding(dbref.getId().toString(), bindings); } else if (value instanceof DBObject) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java index 7aeb09014b..1e836c24db 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -17,6 +17,9 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.junit.Assume.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; import org.junit.Before; import org.junit.Test; @@ -44,6 +47,7 @@ * Integration tests for {@link MongoDbFactoryParser}. * * @author Oliver Gierke + * @auhtor Christoph Strobl */ public class MongoDbFactoryParserIntegrationTests { @@ -132,9 +136,11 @@ public void createsDbFactoryBean() { @Test public void parsesMaxAutoConnectRetryTimeCorrectly() { + assumeFalse(isMongo3Driver()); + reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml")); Mongo mongo = factory.getBean(Mongo.class); - assertThat(mongo.getMongoOptions().maxAutoConnectRetryTime, is(27L)); + assertThat(getMaxAutoConnectRetryTime(mongo.getMongoOptions()), is(27L)); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java index 2df29615dd..b63c08e7f2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 the original author or authors. + * Copyright 2010-2015 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. @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.config; import static org.junit.Assert.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.test.util.ReflectionTestUtils.*; import javax.net.ssl.SSLSocketFactory; @@ -26,6 +27,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker; import org.springframework.data.mongodb.core.MongoFactoryBean; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.convert.MongoConverter; @@ -44,6 +46,7 @@ * @author Oliver Gierke * @author Martin Baumgartner * @author Thomas Darimont + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -239,14 +242,21 @@ public void testMongoSingletonWithPropertyPlaceHolders() throws Exception { assertEquals(8, mongoOpts.connectionsPerHost); assertEquals(1000, mongoOpts.connectTimeout); assertEquals(1500, mongoOpts.maxWaitTime); - assertEquals(true, mongoOpts.autoConnectRetry); + assertEquals(1500, mongoOpts.socketTimeout); assertEquals(4, mongoOpts.threadsAllowedToBlockForConnectionMultiplier); assertEquals(true, mongoOpts.socketKeepAlive); - assertEquals(true, mongoOpts.fsync); - assertEquals(true, mongoOpts.slaveOk); + assertEquals(1, mongoOpts.getWriteConcern().getW()); assertEquals(0, mongoOpts.getWriteConcern().getWtimeout()); assertEquals(true, mongoOpts.getWriteConcern().fsync()); + + if (!isMongo3Driver()) { + assertEquals(true, mongoOpts.fsync); + assertEquals(true, ReflectiveMongoOptionsInvoker.getAutoConnectRetry(mongoOpts)); + assertEquals(true, ReflectiveMongoOptionsInvoker.getSlaveOk(mongoOpts)); + } else { + assertEquals(false, mongoOpts.fsync); + } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MyWriteConcern.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MyWriteConcern.java index c3ddb7f667..e2bc6c2529 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MyWriteConcern.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MyWriteConcern.java @@ -6,7 +6,6 @@ public class MyWriteConcern { public MyWriteConcern(WriteConcern wc) { this._w = wc.getWObject(); - this._continueOnErrorForInsert = wc.getContinueOnErrorForInsert(); this._fsync = wc.getFsync(); this._j = wc.getJ(); this._wtimeout = wc.getWtimeout(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java index f71bd09d1f..7043a72a6a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -17,6 +17,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.springframework.data.mongodb.ReflectiveDBCollectionInvoker.*; import org.junit.Before; import org.junit.Test; @@ -74,9 +75,8 @@ private IndexInfo findAndReturnIndexInfo(DBObject keys) { return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys); } - @SuppressWarnings("deprecation") private static IndexInfo findAndReturnIndexInfo(Iterable candidates, DBObject keys) { - return findAndReturnIndexInfo(candidates, DBCollection.genIndexName(keys)); + return findAndReturnIndexInfo(candidates, generateIndexName(keys)); } private static IndexInfo findAndReturnIndexInfo(Iterable candidates, String name) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java index 9a3109de12..88654971a4 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2015 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. @@ -17,6 +17,8 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.junit.Assume.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; import java.util.ArrayList; import java.util.List; @@ -28,6 +30,7 @@ import org.junit.Test; import org.springframework.dao.DataAccessException; import org.springframework.data.authentication.UserCredentials; +import org.springframework.data.mongodb.ReflectiveDbInvoker; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import com.mongodb.DB; @@ -40,6 +43,7 @@ * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ public class MongoDbUtilsIntegrationTests { @@ -67,6 +71,8 @@ public static void setUp() throws Exception { factory.afterPropertiesSet(); service = factory.getObject(); + + assumeFalse(isMongo3Driver()); } @AfterClass @@ -93,7 +99,8 @@ public void authenticatesCorrectlyInMultithreadedEnvironment() throws Exception // Create sample user template.execute(new DbCallback() { public Void doInDB(DB db) throws MongoException, DataAccessException { - db.addUser("admin", "admin".toCharArray()); + + ReflectiveDbInvoker.addUser(db, "admin", "admin".toCharArray()); return null; } }); @@ -134,7 +141,8 @@ public void authenticatesCorrectlyWithAuthenticationDB() throws Exception { // Create sample user template.execute(new DbCallback() { public Void doInDB(DB db) throws MongoException, DataAccessException { - db.getSisterDB("admin").addUser("admin", "admin".toCharArray()); + + ReflectiveDbInvoker.addUser(db.getSisterDB("admin"), "admin", "admin".toCharArray()); return null; } }); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java index 1342aee924..6f6fa5e766 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2015 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. @@ -17,6 +17,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import java.io.IOException; import java.net.UnknownHostException; @@ -34,10 +35,10 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.mongodb.UncategorizedMongoDbException; +import com.mongodb.MongoCursorNotFoundException; import com.mongodb.MongoException; -import com.mongodb.MongoException.DuplicateKey; -import com.mongodb.MongoException.Network; import com.mongodb.MongoInternalException; +import com.mongodb.MongoSocketException; import com.mongodb.ServerAddress; /** @@ -45,13 +46,16 @@ * * @author Michal Vich * @author Oliver Gierke + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public class MongoExceptionTranslatorUnitTests { MongoExceptionTranslator translator; - @Mock DuplicateKey exception; + @Mock com.mongodb.DuplicateKeyException exception; + @Mock MongoSocketException socketException; + @Mock MongoCursorNotFoundException cursorNotFoundException; @Before public void setUp() { @@ -66,10 +70,11 @@ public void translateDuplicateKey() { } @Test - public void translateNetwork() { + public void translateSocketException() { - Network exception = new Network("IOException", new IOException("IOException")); - DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + when(socketException.getMessage()).thenReturn("IOException"); + when(socketException.getCause()).thenReturn(new IOException("IOException")); + DataAccessException translatedException = translator.translateExceptionIfPossible(socketException); expectExceptionWithCauseMessage(translatedException, DataAccessResourceFailureException.class, "IOException"); @@ -78,8 +83,10 @@ public void translateNetwork() { @Test public void translateCursorNotFound() throws UnknownHostException { - MongoException.CursorNotFound exception = new MongoException.CursorNotFound(1, new ServerAddress()); - DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + when(cursorNotFoundException.getCode()).thenReturn(1); + when(cursorNotFoundException.getServerAddress()).thenReturn(new ServerAddress()); + + DataAccessException translatedException = translator.translateExceptionIfPossible(cursorNotFoundException); expectExceptionWithCauseMessage(translatedException, DataAccessResourceFailureException.class); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java index a73845b0d5..4d3f73b9e7 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -17,6 +17,9 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.junit.Assume.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; import javax.net.ssl.SSLSocketFactory; @@ -29,6 +32,7 @@ * * @author Oliver Gierke * @author Mike Saavedra + * @author Christoph Strobl */ public class MongoOptionsFactoryBeanUnitTests { @@ -38,12 +42,14 @@ public class MongoOptionsFactoryBeanUnitTests { @Test public void setsMaxConnectRetryTime() { + assumeFalse(isMongo3Driver()); + MongoOptionsFactoryBean bean = new MongoOptionsFactoryBean(); bean.setMaxAutoConnectRetryTime(27); bean.afterPropertiesSet(); MongoOptions options = bean.getObject(); - assertThat(options.maxAutoConnectRetryTime, is(27L)); + assertThat(getMaxAutoConnectRetryTime(options), is(27L)); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 03c23ba2e1..799c290a1d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -18,7 +18,9 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.*; -import static org.mockito.Mockito.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; +import static org.springframework.data.mongodb.ReflectiveWriteConcernInvoker.*; +import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; 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.*; @@ -225,7 +227,11 @@ public void bogusUpdateDoesNotTriggerException() throws Exception { public void throwsExceptionForDuplicateIds() { MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + if (isMongo3Driver()) { + template.setWriteConcern(WriteConcern.ACKNOWLEDGED); + } else { + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + } Person person = new Person(new ObjectId(), "Amol"); person.setAge(28); @@ -248,7 +254,11 @@ public void throwsExceptionForDuplicateIds() { public void throwsExceptionForUpdateWithInvalidPushOperator() { MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + if (isMongo3Driver()) { + template.setWriteConcern(WriteConcern.ACKNOWLEDGED); + } else { + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + } ObjectId id = new ObjectId(); Person person = new Person(id, "Amol"); @@ -257,10 +267,8 @@ public void throwsExceptionForUpdateWithInvalidPushOperator() { template.insert(person); thrown.expect(DataIntegrityViolationException.class); - thrown.expectMessage("Execution"); - thrown.expectMessage("UPDATE"); thrown.expectMessage("array"); - thrown.expectMessage("firstName"); + thrown.expectMessage("age"); thrown.expectMessage("failed"); Query query = new Query(Criteria.where("firstName").is("Amol")); @@ -275,7 +283,11 @@ public void throwsExceptionForUpdateWithInvalidPushOperator() { public void throwsExceptionForIndexViolationIfConfigured() { MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + if (isMongo3Driver()) { + template.setWriteConcern(WriteConcern.ACKNOWLEDGED); + } else { + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + } template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Direction.DESC).unique()); Person person = new Person(new ObjectId(), "Amol"); @@ -304,7 +316,11 @@ public void rejectsDuplicateIdInInsertAll() { thrown.expectMessage("E11000 duplicate key error index: database.person.$_id_"); MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + if (isMongo3Driver()) { + template.setWriteConcern(WriteConcern.ACKNOWLEDGED); + } else { + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + } ObjectId id = new ObjectId(); Person person = new Person(id, "Amol"); @@ -378,7 +394,7 @@ public void testEnsureIndex() throws Exception { public void testReadIndexInfoForIndicesCreatedViaMongoShellCommands() throws Exception { String command = "db." + template.getCollectionName(Person.class) - + ".ensureIndex({'age':-1}, {'unique':true, 'sparse':true})"; + + ".createIndex({'age':-1}, {'unique':true, 'sparse':true})"; template.indexOps(Person.class).dropAllIndexes(); assertThat(template.indexOps(Person.class).getIndexInfo().isEmpty(), is(true)); @@ -995,7 +1011,9 @@ public void testUsingUpdateWithMultipleSet() throws Exception { WriteResult wr = template.updateMulti(new Query(), u, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(wr.getN(), is(2)); + if (wasAcknowledged(wr)) { + assertThat(wr.getN(), is(2)); + } Query q1 = new Query(Criteria.where("age").in(11, 21)); List r1 = template.find(q1, PersonWithIdPropertyOfTypeObjectId.class); @@ -1097,7 +1115,6 @@ public void testFindOneWithSort() { } @Test - @SuppressWarnings("deprecation") public void testUsingReadPreference() throws Exception { this.template.execute("readPref", new CollectionCallback() { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { @@ -1107,10 +1124,10 @@ public Object doInCollection(DBCollection collection) throws MongoException, Dat } }); MongoTemplate slaveTemplate = new MongoTemplate(factory); - slaveTemplate.setReadPreference(ReadPreference.SECONDARY); + slaveTemplate.setReadPreference(ReadPreference.secondary()); slaveTemplate.execute("readPref", new CollectionCallback() { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { - assertThat(collection.getReadPreference(), is(ReadPreference.SECONDARY)); + assertThat(collection.getReadPreference(), is(ReadPreference.secondary())); assertThat(collection.getDB().getOptions(), is(0)); return null; } @@ -1153,29 +1170,24 @@ public void testWriteConcernResolver() { person.setId(new ObjectId()); person.setFirstName("Dave"); - template.setWriteConcern(WriteConcern.NONE); + template.setWriteConcern(noneOrUnacknowledged()); template.save(person); WriteResult result = template.updateFirst(query(where("id").is(person.getId())), update("firstName", "Carter"), PersonWithIdPropertyOfTypeObjectId.class); - WriteConcern lastWriteConcern = result.getLastConcern(); - assertThat(lastWriteConcern, equalTo(WriteConcern.NONE)); FsyncSafeWriteConcernResolver resolver = new FsyncSafeWriteConcernResolver(); template.setWriteConcernResolver(resolver); Query q = query(where("_id").is(person.getId())); Update u = update("firstName", "Carter"); result = template.updateFirst(q, u, PersonWithIdPropertyOfTypeObjectId.class); - lastWriteConcern = result.getLastConcern(); - assertThat(lastWriteConcern, equalTo(WriteConcern.FSYNC_SAFE)); MongoAction lastMongoAction = resolver.getMongoAction(); assertThat(lastMongoAction.getCollectionName(), is("personWithIdPropertyOfTypeObjectId")); - assertThat(lastMongoAction.getDefaultWriteConcern(), equalTo(WriteConcern.NONE)); + assertThat(lastMongoAction.getDefaultWriteConcern(), equalTo(noneOrUnacknowledged())); assertThat(lastMongoAction.getDocument(), notNullValue()); assertThat(lastMongoAction.getEntityType().toString(), is(PersonWithIdPropertyOfTypeObjectId.class.toString())); assertThat(lastMongoAction.getMongoActionOperation(), is(MongoActionOperation.UPDATE)); assertThat(lastMongoAction.getQuery(), equalTo(q.getQueryObject())); - } private class FsyncSafeWriteConcernResolver implements WriteConcernResolver { @@ -1198,8 +1210,8 @@ public MongoAction getMongoAction() { @Test public void updatesDBRefsCorrectly() { - DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId()); - DBRef second = new DBRef(factory.getDb(), "bar", new ObjectId()); + DBRef first = new DBRef("foo", new ObjectId()); + DBRef second = new DBRef("bar", new ObjectId()); template.updateFirst(null, update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); } @@ -1715,29 +1727,6 @@ public void nullsValuesForUpdatesOfUnversionedEntity() { assertThat(person.getFirstName(), is(nullValue())); } - /** - * @see DATAMONGO-651 - */ - @Test - public void throwsMongoSpecificExceptionForDataIntegrityViolations() { - - WriteResult result = mock(WriteResult.class); - when(result.getError()).thenReturn("ERROR"); - - MongoActionOperation operation = MongoActionOperation.INSERT; - - MongoTemplate mongoTemplate = new MongoTemplate(factory); - mongoTemplate.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - try { - mongoTemplate.handleAnyWriteResultErrors(result, null, operation); - fail("Expected MonogoDataIntegrityViolationException!"); - } catch (MongoDataIntegrityViolationException o_O) { - assertThat(o_O.getActionOperation(), is(operation)); - assertThat(o_O.getWriteResult(), is(result)); - } - } - /** * @see DATAMONGO-679 */ diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java index 89e1769757..de2ec283c9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2015 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. @@ -17,6 +17,8 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.util.ReflectionTestUtils.*; import java.net.UnknownHostException; @@ -26,15 +28,17 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.test.util.ReflectionTestUtils; import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientURI; import com.mongodb.MongoURI; /** * Unit tests for {@link SimpleMongoDbFactory}. * * @author Oliver Gierke + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public class SimpleMongoDbFactoryUnitTests { @@ -54,6 +58,7 @@ public void rejectsIllegalDatabaseNames() { * @see DATADOC-254 */ @Test + @SuppressWarnings("deprecation") public void allowsDatabaseNames() { new SimpleMongoDbFactory(mongo, "foo-bar"); new SimpleMongoDbFactory(mongo, "foo_bar"); @@ -71,22 +76,46 @@ public void mongoUriConstructor() throws UnknownHostException { MongoURI mongoURI = new MongoURI("mongodb://myUsername:myPassword@localhost/myDatabase.myCollection"); MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoURI); - assertThat(ReflectionTestUtils.getField(mongoDbFactory, "credentials"), is((Object) new UserCredentials( - "myUsername", "myPassword"))); - assertThat(ReflectionTestUtils.getField(mongoDbFactory, "databaseName").toString(), is("myDatabase")); - assertThat(ReflectionTestUtils.getField(mongoDbFactory, "databaseName").toString(), is("myDatabase")); + assertThat(getField(mongoDbFactory, "credentials"), is((Object) new UserCredentials("myUsername", "myPassword"))); + assertThat(getField(mongoDbFactory, "databaseName").toString(), is("myDatabase")); } /** * @see DATAMONGO-789 */ @Test + @SuppressWarnings("deprecation") public void defaultsAuthenticationDatabaseToDatabase() { SimpleMongoDbFactory factory = new SimpleMongoDbFactory(mongo, "foo"); - assertThat(ReflectionTestUtils.getField(factory, "authenticationDatabaseName"), is((Object) "foo")); + assertThat(getField(factory, "authenticationDatabaseName"), is((Object) "foo")); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void constructsMongoClientAccordingToMongoUri() throws UnknownHostException { + + MongoClientURI uri = new MongoClientURI("mongodb://myUserName:myPassWord@127.0.0.1:27017/myDataBase.myCollection"); + SimpleMongoDbFactory factory = new SimpleMongoDbFactory(uri); + + assertThat(getField(factory, "databaseName").toString(), is("myDataBase")); } + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldDefaultAuthenticationDbNameToDbNameWhenUsingMongoClient() throws UnknownHostException { + + MongoClient clientMock = mock(MongoClient.class); + SimpleMongoDbFactory factory = new SimpleMongoDbFactory(clientMock, "FooBar"); + + assertThat(getField(factory, "authenticationDatabaseName").toString(), is("FooBar")); + } + + @SuppressWarnings("deprecation") private void rejectsDatabaseName(String databaseName) { try { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java index 1d612b69c6..799e0c6b6d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java @@ -75,6 +75,7 @@ * @author Tobias Trelle * @author Thomas Darimont * @author Oliver Gierke + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -202,7 +203,6 @@ public void shouldAggregate() { AggregationResults results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); - assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017")); List tagCount = results.getMappedResults(); @@ -230,7 +230,6 @@ public void shouldAggregateEmptyCollection() { AggregationResults results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); - assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017")); List tagCount = results.getMappedResults(); @@ -254,7 +253,6 @@ public void shouldDetectResultMismatch() { AggregationResults results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); - assertThat(results.getServerUsed(), endsWith("127.0.0.1:27017")); List tagCount = results.getMappedResults(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 8b9682c15b..8aa8702454 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -42,6 +42,7 @@ import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.mongodb.MongoClientVersion; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.MongoExceptionTranslator; import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person; @@ -52,14 +53,17 @@ import org.springframework.util.SerializationUtils; import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; import com.mongodb.DBObject; import com.mongodb.DBRef; /** - * Unit tests dor {@link DbRefMappingMongoConverterUnitTests}. + * Unit tests for {@link DbRefMappingMongoConverter}. * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public class DbRefMappingMongoConverterUnitTests { @@ -68,14 +72,16 @@ public class DbRefMappingMongoConverterUnitTests { MongoMappingContext mappingContext; @Mock MongoDbFactory dbFactory; + DefaultDbRefResolver dbRefResolver; @Before public void setUp() { when(dbFactory.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); + this.dbRefResolver = spy(new DefaultDbRefResolver(dbFactory)); this.mappingContext = new MongoMappingContext(); - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(dbFactory), mappingContext); + this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); } /** @@ -89,7 +95,7 @@ public void createsSimpleDBRefCorrectly() { DBRef dbRef = converter.toDBRef(person, null); assertThat(dbRef.getId(), is((Object) "foo")); - assertThat(dbRef.getRef(), is("person")); + assertThat(dbRef.getCollectionName(), is("person")); } /** @@ -98,6 +104,21 @@ public void createsSimpleDBRefCorrectly() { @Test public void convertDocumentWithMapDBRef() { + DBObject mapValDBObject = new BasicDBObject(); + mapValDBObject.put("_id", BigInteger.ONE); + + DBRef dbRef = mock(DBRef.class); + + if (MongoClientVersion.isMongo3Driver()) { + DB dbMock = mock(DB.class); + DBCollection collectionMock = mock(DBCollection.class); + when(dbFactory.getDb()).thenReturn(dbMock); + when(dbMock.getCollection(anyString())).thenReturn(collectionMock); + when(collectionMock.findOne(anyObject())).thenReturn(mapValDBObject); + } else { + when(dbRefResolver.fetch(dbRef)).thenReturn(mapValDBObject); + } + MapDBRef mapDBRef = new MapDBRef(); MapDBRefVal val = new MapDBRefVal(); @@ -115,12 +136,6 @@ public void convertDocumentWithMapDBRef() { assertThat(map.get("test"), instanceOf(DBRef.class)); - DBObject mapValDBObject = new BasicDBObject(); - mapValDBObject.put("_id", BigInteger.ONE); - - DBRef dbRef = mock(DBRef.class); - when(dbRef.fetch()).thenReturn(mapValDBObject); - ((DBObject) dbObject.get("map")).put("test", dbRef); MapDBRef read = converter.read(MapDBRef.class, dbObject); @@ -142,7 +157,7 @@ public void createsDBRefWithClientSpecCorrectly() { DBRef dbRef = converter.toDBRef(person, property); assertThat(dbRef.getId(), is((Object) "foo")); - assertThat(dbRef.getRef(), is("person")); + assertThat(dbRef.getCollectionName(), is("person")); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 7ca8ec83f6..49bfbb2cdf 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -91,7 +91,6 @@ import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; -import com.mongodb.DB; import com.mongodb.DBObject; import com.mongodb.DBRef; import com.mongodb.util.JSON; @@ -1092,7 +1091,7 @@ public void readEmptyCollectionIsModifiable() { @Test public void readsPlainDBRefObject() { - DBRef dbRef = new DBRef(mock(DB.class), "foo", 2); + DBRef dbRef = new DBRef("foo", 2); DBObject dbObject = new BasicDBObject("ref", dbRef); DBRefWrapper result = converter.read(DBRefWrapper.class, dbObject); @@ -1105,7 +1104,7 @@ public void readsPlainDBRefObject() { @Test public void readsCollectionOfDBRefs() { - DBRef dbRef = new DBRef(mock(DB.class), "foo", 2); + DBRef dbRef = new DBRef("foo", 2); BasicDBList refs = new BasicDBList(); refs.add(dbRef); @@ -1139,8 +1138,8 @@ public void readsDBRefMap() { @SuppressWarnings({ "rawtypes", "unchecked" }) public void resolvesDBRefMapValue() { + when(resolver.fetch(Mockito.any(DBRef.class))).thenReturn(new BasicDBObject()); DBRef dbRef = mock(DBRef.class); - when(dbRef.fetch()).thenReturn(new BasicDBObject()); BasicDBObject refMap = new BasicDBObject("foo", dbRef); DBObject dbObject = new BasicDBObject("personMap", refMap); @@ -1267,8 +1266,7 @@ private static void assertSyntheticFieldValueOf(Object target, Object expected) @Test public void eagerlyReturnsDBRefObjectIfTargetAlreadyIsOne() { - DB db = mock(DB.class); - DBRef dbRef = new DBRef(db, "collection", "id"); + DBRef dbRef = new DBRef("collection", "id"); MongoPersistentProperty property = mock(MongoPersistentProperty.class); @@ -1869,7 +1867,7 @@ public void rejectsBasicDbListToBeConvertedIntoComplexType() { public void readShouldRespectExplicitFieldNameForDbRef() { BasicDBObject source = new BasicDBObject(); - source.append("explict-name-for-db-ref", new DBRef(mock(DB.class), "foo", "1")); + source.append("explict-name-for-db-ref", new DBRef("foo", "1")); converter.read(ClassWithExplicitlyNamedDBRefProperty.class, source); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index 78f2501926..eaf70bda0e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -383,7 +383,7 @@ public void doesNotMapIdIfNoEntityMetadataAvailable() { public void handleMapWithDBRefCorrectly() { DBObject mapDbObject = new BasicDBObject(); - mapDbObject.put("test", new com.mongodb.DBRef(null, "test", "test")); + mapDbObject.put("test", new com.mongodb.DBRef("test", "test")); DBObject dbObject = new BasicDBObject(); dbObject.put("mapWithDBRef", mapDbObject); @@ -666,7 +666,7 @@ public void mapsIdReferenceToDBRefCorrectly() { ObjectId id = new ObjectId(); - DBObject query = new BasicDBObject("reference.id", new com.mongodb.DBRef(null, "reference", id.toString())); + DBObject query = new BasicDBObject("reference.id", new com.mongodb.DBRef("reference", id.toString())); DBObject result = mapper.getMappedObject(query, context.getPersistentEntity(WithDBRef.class)); assertThat(result.containsField("reference"), is(true)); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index ffe1262050..d2cc763bbe 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2015 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. @@ -69,7 +69,6 @@ public class UpdateMapperUnitTests { private Converter writingConverterSpy; - @SuppressWarnings("unchecked") @Before public void setUp() { @@ -392,7 +391,7 @@ public void createsDbRefForEntityIdOnPulls() { context.getPersistentEntity(DocumentWithDBRefCollection.class)); DBObject pullClause = getAsDBObject(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef(null, "entity", "2"))); + assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef("entity", "2"))); } /** @@ -409,7 +408,7 @@ public void createsDbRefForEntityOnPulls() { context.getPersistentEntity(DocumentWithDBRefCollection.class)); DBObject pullClause = getAsDBObject(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef(null, "entity", entity.id))); + assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef("entity", entity.id))); } /** @@ -450,7 +449,7 @@ public void rendersUpdateOfDbRefPropertyWithDomainObjectCorrectly() { context.getPersistentEntity(DocumentWithDBRefCollection.class)); DBObject setClause = getAsDBObject(mappedObject, "$set"); - assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef(null, "entity", entity.id))); + assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef("entity", entity.id))); } /** @@ -541,7 +540,7 @@ public void updateOnDbrefPropertyOfInterfaceTypeWithoutExplicitGetterForIdShould DBObject $set = DBObjectTestUtils.getAsDBObject(mappedObject, "$set"); Object model = $set.get("referencedDocument"); - DBRef expectedDBRef = new DBRef(factory.getDb(), "interfaceDocumentDefinitionImpl", "1"); + DBRef expectedDBRef = new DBRef("interfaceDocumentDefinitionImpl", "1"); assertThat(model, allOf(instanceOf(DBRef.class), IsEqual. equalTo(expectedDBRef))); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java index 60e0eebfb9..96e75870b6 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -166,11 +166,6 @@ public void simpleGroupWithQueryAndFunctionsAsResources() { private void assertMapReduceResults(GroupByResults results) { - DBObject dboRawResults = results.getRawResults(); - - assertThat(dboRawResults.containsField("serverUsed"), is(true)); - assertThat(dboRawResults.get("serverUsed").toString(), endsWith("127.0.0.1:27017")); - int numResults = 0; for (XObject xObject : results) { if (xObject.getX() == 1) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java index d130cbbaff..47cf1eec96 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2015 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. @@ -17,6 +17,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; +import static org.springframework.util.Assert.*; import java.text.DecimalFormat; import java.util.ArrayList; @@ -46,7 +47,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; -import org.springframework.util.Assert; import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; @@ -65,6 +65,7 @@ * abstraction. * * @author Oliver Gierke + * @author Christoph Strobl */ public class PerformanceTests { @@ -270,8 +271,8 @@ private void setupCollections() { DBCollection collection = db.getCollection(collectionName); collection.drop(); collection.getDB().command(getCreateCollectionCommand(collectionName)); - collection.ensureIndex(new BasicDBObject("firstname", -1)); - collection.ensureIndex(new BasicDBObject("lastname", -1)); + collection.createIndex(new BasicDBObject("firstname", -1)); + collection.createIndex(new BasicDBObject("lastname", -1)); } } @@ -621,7 +622,7 @@ public DBObject toDBObject() { private static List pickRandomNumerOfItemsFrom(List source) { - Assert.isTrue(!source.isEmpty()); + isTrue(!source.isEmpty()); Random random = new Random(); int numberOfItems = random.nextInt(source.size()); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java index 36f66ef3ac..4692b0ef25 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -43,6 +43,7 @@ * Unit tests for {@link ConvertingParameterAccessor}. * * @author Oliver Gierke + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public class ConvertingParameterAccessorUnitTests { @@ -105,7 +106,7 @@ public void convertsAssociationsToDBRef() { assertThat(result, is(instanceOf(com.mongodb.DBRef.class))); com.mongodb.DBRef dbRef = (com.mongodb.DBRef) result; - assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getCollectionName(), is("property")); assertThat(dbRef.getId(), is((Object) 5L)); } @@ -128,7 +129,7 @@ public void convertsAssociationsToDBRefForCollections() { assertThat(element, is(instanceOf(com.mongodb.DBRef.class))); com.mongodb.DBRef dbRef = (com.mongodb.DBRef) element; - assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getCollectionName(), is("property")); assertThat(dbRef.getId(), is((Object) 5L)); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index cc5777141d..769dfd3d41 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -238,7 +238,7 @@ public void createsOrQueryCorrectly() { public void createsQueryReferencingADBRefCorrectly() { User user = new User(); - com.mongodb.DBRef dbref = new com.mongodb.DBRef(null, "user", "id"); + com.mongodb.DBRef dbref = new com.mongodb.DBRef("user", "id"); when(converter.toDBRef(eq(user), Mockito.any(MongoPersistentProperty.class))).thenReturn(dbref); PartTree tree = new PartTree("findByCreator", User.class); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java index 331ef08c2b..b25cabd4b3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -271,7 +271,7 @@ public void parsesDbRefDeclarationsCorrectly() throws Exception { DBRef dbRef = DBObjectTestUtils.getTypedValue(query.getQueryObject(), "reference", DBRef.class); assertThat(dbRef.getId(), is((Object) "myid")); - assertThat(dbRef.getRef(), is("reference")); + assertThat(dbRef.getCollectionName(), is("reference")); } /** diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index 94f50875a0..a75b359e87 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/src/main/asciidoc/reference/mapping.adoc @@ -167,7 +167,7 @@ public class Person { ---- ==== -IMPORTANT: The `@Id` annotation tells the mapper which property you want to use for the MongoDB `_id` property and the `@Indexed` annotation tells the mapping framework to call `ensureIndex` on that property of your document, making searches faster. +IMPORTANT: The `@Id` annotation tells the mapper which property you want to use for the MongoDB `_id` property and the `@Indexed` annotation tells the mapping framework to call `createIndex` on that property of your document, making searches faster. IMPORTANT: Automatic index creation is only done for types annotated with `@Document`. diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index a6bf76b01a..6697716dbb 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1159,7 +1159,7 @@ Before we are actually able to use full text search we have to ensure to set up [source,javascript] ---- -db.foo.ensureIndex( +db.foo.createIndex( { title : "text", content : "text" From 7595b0fdec03284c0e776ed06d12d358f28d2ec0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 Feb 2015 09:07:25 +0100 Subject: [PATCH 3/8] DATAMONGO-1158 - Add Support for mongo-java-driver 3.0 We now support mongo-java-driver version 2.x and 3.0 along with MongoDB Server 2.6.7 and 3.0.0. Pleaes note that some of the configurations options might no longer be valid when used with version 3 of the mongo-java-driver. Have a look at the table below so see some of the major differences in using version 2.x or 3.0. | 2.x | 3.0 ----------------------+-----------------------+----------------------------------------------- default WriteConcern | NONE | UNACKNOWLEDGED ----------------------+-----------------------+----------------------------------------------- option for slaveOk | available | ignored ----------------------+-----------------------+----------------------------------------------- option for autoConnect| available | ignored ----------------------+-----------------------+----------------------------------------------- write result checking | available | defaults WriteConncern to ACKNOWLEGED for EXCEPTION ----------------------+-----------------------+----------------------------------------------- rest index cache | available | throws UnsupportedOperationException ----------------------+-----------------------+----------------------------------------------- DBRef resolution | via DBRef.fetch | via collection.findOne ----------------------+-----------------------+----------------------------------------------- MapReduce Options | applied | ignored ----------------------+-----------------------+----------------------------------------------- authentication | via UserCredentials | via MongoClient ----------------------+-----------------------+----------------------------------------------- WriteConcernException | not available | translated to DataIntegretyViolationException ----------------------+-----------------------+----------------------------------------------- executeInSession | available | requestStart/requestDone commands ignored. ----------------------+-----------------------+----------------------------------------------- index creation | via createIndex | via createIndex ----------------------+-----------------------+----------------------------------------------- --- .../data/mongodb/core/MongoTemplate.java | 15 +++++++++-- .../data/mongodb/core/MongoTemplateTests.java | 25 +++---------------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 53cf65b8b9..e5c74f2013 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -16,11 +16,13 @@ package org.springframework.data.mongodb.core; import static com.mongodb.ReadPreference.*; +import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.data.mongodb.ReflectiveDbInvoker.*; import static org.springframework.data.mongodb.ReflectiveMapReduceInvoker.*; import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.SerializationUtils.*; +import static org.springframework.util.ObjectUtils.*; import java.io.IOException; import java.util.ArrayList; @@ -723,13 +725,22 @@ protected void prepareCollection(DBCollection collection) { /** * Prepare the WriteConcern before any processing is done using it. This allows a convenient way to apply custom - * settings in sub-classes. + * settings in sub-classes.
+ * In case of using mongo-java-driver version 3 the returned {@link WriteConcern} will be defaulted to + * {@link WriteConcern#ACKNOWLEDGED} when {@link WriteResultChecking} is set to {@link WriteResultChecking#EXCEPTION}. * * @param writeConcern any WriteConcern already configured or null * @return The prepared WriteConcern or null */ protected WriteConcern prepareWriteConcern(MongoAction mongoAction) { - return writeConcernResolver.resolve(mongoAction); + + WriteConcern wc = writeConcernResolver.resolve(mongoAction); + + if (isMongo3Driver() && nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking) + && (wc == null || wc.getW() < 1)) { + return WriteConcern.ACKNOWLEDGED; + } + return wc; } protected void doInsert(String collectionName, T objectToSave, MongoWriter writer) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 799c290a1d..90653a2a2b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -18,7 +18,6 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.*; -import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.data.mongodb.ReflectiveWriteConcernInvoker.*; import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; import static org.springframework.data.mongodb.core.query.Criteria.*; @@ -227,11 +226,7 @@ public void bogusUpdateDoesNotTriggerException() throws Exception { public void throwsExceptionForDuplicateIds() { MongoTemplate template = new MongoTemplate(factory); - if (isMongo3Driver()) { - template.setWriteConcern(WriteConcern.ACKNOWLEDGED); - } else { - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - } + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); Person person = new Person(new ObjectId(), "Amol"); person.setAge(28); @@ -254,11 +249,7 @@ public void throwsExceptionForDuplicateIds() { public void throwsExceptionForUpdateWithInvalidPushOperator() { MongoTemplate template = new MongoTemplate(factory); - if (isMongo3Driver()) { - template.setWriteConcern(WriteConcern.ACKNOWLEDGED); - } else { - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - } + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); ObjectId id = new ObjectId(); Person person = new Person(id, "Amol"); @@ -283,11 +274,7 @@ public void throwsExceptionForUpdateWithInvalidPushOperator() { public void throwsExceptionForIndexViolationIfConfigured() { MongoTemplate template = new MongoTemplate(factory); - if (isMongo3Driver()) { - template.setWriteConcern(WriteConcern.ACKNOWLEDGED); - } else { - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - } + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Direction.DESC).unique()); Person person = new Person(new ObjectId(), "Amol"); @@ -316,11 +303,7 @@ public void rejectsDuplicateIdInInsertAll() { thrown.expectMessage("E11000 duplicate key error index: database.person.$_id_"); MongoTemplate template = new MongoTemplate(factory); - if (isMongo3Driver()) { - template.setWriteConcern(WriteConcern.ACKNOWLEDGED); - } else { - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - } + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); ObjectId id = new ObjectId(); Person person = new Person(id, "Amol"); From 6e6686d2bd8a8e050e25fa46c4312c2845cec73f Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 20 Feb 2015 08:27:34 +0100 Subject: [PATCH 4/8] DATAMONGO-1158 - Soften exception validation. We need to soften the exception validation a bit since the message is slightly different when using different storage engines in a MongoDB 3.0 environment. --- .../data/mongodb/core/MongoTemplateTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 90653a2a2b..337c6df051 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -237,7 +237,7 @@ public void throwsExceptionForDuplicateIds() { template.insert(person); fail("Expected DataIntegrityViolationException!"); } catch (DataIntegrityViolationException e) { - assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_")); + assertThat(e.getMessage(), containsString("E11000 duplicate key error")); } } @@ -289,7 +289,7 @@ public void throwsExceptionForIndexViolationIfConfigured() { template.save(person); fail("Expected DataIntegrityViolationException!"); } catch (DataIntegrityViolationException e) { - assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$firstName_-1")); + assertThat(e.getMessage(), containsString("E11000 duplicate key error")); } } @@ -300,7 +300,7 @@ public void throwsExceptionForIndexViolationIfConfigured() { public void rejectsDuplicateIdInInsertAll() { thrown.expect(DataIntegrityViolationException.class); - thrown.expectMessage("E11000 duplicate key error index: database.person.$_id_"); + thrown.expectMessage("E11000 duplicate key error"); MongoTemplate template = new MongoTemplate(factory); template.setWriteResultChecking(WriteResultChecking.EXCEPTION); From 9f43d207df1a39831ed5eb8a54113537aa480318 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 23 Feb 2015 10:36:55 +0100 Subject: [PATCH 5/8] DATAMONGO-1158 - Polishing Round I * protected reflective invokers by moving them around. * undo static imports. * add usage notes to reference documentation. --- .../data/mongodb/MongoClientVersion.java | 6 +- .../mongodb/core/DefaultIndexOperations.java | 1 - .../data/mongodb/core/MongoDbUtils.java | 20 +++---- .../core/MongoExceptionTranslator.java | 23 ++++---- .../mongodb/core/MongoOptionsFactoryBean.java | 16 ++--- .../data/mongodb/core/MongoTemplate.java | 24 ++++---- .../ReflectiveDBCollectionInvoker.java | 6 +- .../{ => core}/ReflectiveDbInvoker.java | 34 +++++------ .../ReflectiveMapReduceInvoker.java | 21 +++---- .../ReflectiveMongoOptionsInvoker.java | 40 +++++++------ .../ReflectiveWriteConcernInvoker.java | 11 ++-- .../ReflectiveWriteResultInvoker.java | 13 ++-- .../mongodb/core/SimpleMongoDbFactory.java | 18 +++--- .../core/convert/DefaultDbRefResolver.java | 1 - .../convert}/ReflectiveDBRefResolver.java | 13 ++-- .../core/mapreduce/MapReduceResults.java | 12 ++-- .../MongoDbFactoryParserIntegrationTests.java | 4 +- .../mongodb/config/MongoNamespaceTests.java | 6 +- ...efaultIndexOperationsIntegrationTests.java | 2 +- .../core/MongoDbUtilsIntegrationTests.java | 1 - .../MongoOptionsFactoryBeanUnitTests.java | 2 +- .../data/mongodb/core/MongoTemplateTests.java | 4 +- ...ReflectiveMongoOptionsInvokerTestUtil.java | 52 ++++++++++++++++ src/main/asciidoc/index.adoc | 1 + src/main/asciidoc/reference/mongo-3.adoc | 59 +++++++++++++++++++ 25 files changed, 253 insertions(+), 137 deletions(-) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveDBCollectionInvoker.java (95%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveDbInvoker.java (72%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveMapReduceInvoker.java (71%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveMongoOptionsInvoker.java (75%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveWriteConcernInvoker.java (78%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core}/ReflectiveWriteResultInvoker.java (81%) rename spring-data-mongodb/src/main/java/org/springframework/data/mongodb/{ => core/convert}/ReflectiveDBRefResolver.java (81%) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvokerTestUtil.java create mode 100644 src/main/asciidoc/reference/mongo-3.adoc diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java index 70d1af2657..f8c8f1a8a5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoClientVersion.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb; -import static org.springframework.util.ClassUtils.*; +import org.springframework.util.ClassUtils; /** * {@link MongoClientVersion} holds information about the used mongo-java client and is used to distinguish between @@ -26,9 +26,9 @@ */ public class MongoClientVersion { - private static final boolean IS_MONGO_30 = isPresent("com.mongodb.binding.SingleServerBinding", + private static final boolean IS_MONGO_30 = ClassUtils.isPresent("com.mongodb.binding.SingleServerBinding", MongoClientVersion.class.getClassLoader()); - private static final boolean IS_ASYNC_CLIENT = isPresent("com.mongodb.async.client.MongoClient", + private static final boolean IS_ASYNC_CLIENT = ClassUtils.isPresent("com.mongodb.async.client.MongoClient", MongoClientVersion.class.getClassLoader()); /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java index 3f609a16ae..a1f2c96725 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -23,7 +23,6 @@ import java.util.List; import org.springframework.dao.DataAccessException; -import org.springframework.data.mongodb.ReflectiveDBCollectionInvoker; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexField; import org.springframework.data.mongodb.core.index.IndexInfo; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java index 6316d507e2..0985abcf73 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java @@ -15,14 +15,12 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.data.mongodb.ReflectiveDbInvoker.*; -import static org.springframework.util.Assert.*; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.authentication.UserCredentials; +import org.springframework.data.mongodb.MongoClientVersion; import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.util.Assert; import com.mongodb.DB; import com.mongodb.Mongo; @@ -90,10 +88,10 @@ public static DB getDB(Mongo mongo, String databaseName, UserCredentials credent public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials, String authenticationDatabaseName) { - notNull(mongo, "No Mongo instance specified!"); - hasText(databaseName, "Database name must be given!"); - notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!"); - hasText(authenticationDatabaseName, "Authentication database name must not be null or empty!"); + Assert.notNull(mongo, "No Mongo instance specified!"); + Assert.hasText(databaseName, "Database name must be given!"); + Assert.notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!"); + Assert.hasText(authenticationDatabaseName, "Authentication database name must not be null or empty!"); return doGetDB(mongo, databaseName, credentials, true, authenticationDatabaseName); } @@ -128,7 +126,7 @@ private static DB doGetDB(Mongo mongo, String databaseName, UserCredentials cred DB db = mongo.getDB(databaseName); if (requiresAuthDbAuthentication(credentials)) { - authenticate(mongo, db, credentials, authenticationDatabaseName); + ReflectiveDbInvoker.authenticate(mongo, db, credentials, authenticationDatabaseName); } // TX sync active, bind new database to thread @@ -195,7 +193,7 @@ public static void closeDB(DB db) { if (db != null) { LOGGER.debug("Closing Mongo DB object"); try { - requestDone(db); + ReflectiveDbInvoker.requestDone(db); } catch (Throwable ex) { LOGGER.debug("Unexpected exception on closing Mongo DB object", ex); } @@ -215,7 +213,7 @@ private static boolean requiresAuthDbAuthentication(UserCredentials credentials) return false; } - return isMongo3Driver() ? false : true; + return !MongoClientVersion.isMongo3Driver(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java index 4bb292967d..9be051e309 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java @@ -15,9 +15,7 @@ */ package org.springframework.data.mongodb.core; -import static java.util.Arrays.*; -import static org.springframework.util.ClassUtils.*; - +import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -29,6 +27,7 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.UncategorizedMongoDbException; +import org.springframework.util.ClassUtils; import com.mongodb.MongoException; @@ -43,16 +42,18 @@ */ public class MongoExceptionTranslator implements PersistenceExceptionTranslator { - private static final Set DULICATE_KEY_EXCEPTIONS = new HashSet(asList("MongoException.DuplicateKey", - "DuplicateKeyException")); + private static final Set DULICATE_KEY_EXCEPTIONS = new HashSet(Arrays.asList( + "MongoException.DuplicateKey", "DuplicateKeyException")); - private static final Set RESOURCE_FAILURE_EXCEPTIONS = new HashSet(asList("MongoException.Network", - "MongoSocketException", "MongoException.CursorNotFound", "MongoCursorNotFoundException", - "MongoServerSelectionException", "MongoTimeoutException")); + private static final Set RESOURCE_FAILURE_EXCEPTIONS = new HashSet(Arrays.asList( + "MongoException.Network", "MongoSocketException", "MongoException.CursorNotFound", + "MongoCursorNotFoundException", "MongoServerSelectionException", "MongoTimeoutException")); - private static final Set RESOURCE_USAGE_EXCEPTIONS = new HashSet(asList("MongoInternalException")); + private static final Set RESOURCE_USAGE_EXCEPTIONS = new HashSet( + Arrays.asList("MongoInternalException")); - private static final Set DATA_INTEGRETY_EXCEPTIONS = new HashSet(asList("WriteConcernException")); + private static final Set DATA_INTEGRETY_EXCEPTIONS = new HashSet( + Arrays.asList("WriteConcernException")); /* * (non-Javadoc) @@ -62,7 +63,7 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { // Check for well-known MongoException subclasses. - String exception = getShortName(getUserClass(ex.getClass())); + String exception = ClassUtils.getShortName(ClassUtils.getUserClass(ex.getClass())); if (DULICATE_KEY_EXCEPTIONS.contains(exception)) { return new DuplicateKeyException(ex.getMessage(), ex); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java index 79ad28a0a8..0fc2991666 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java @@ -15,14 +15,11 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; - import javax.net.ssl.SSLSocketFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; -import org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker; +import org.springframework.data.mongodb.MongoClientVersion; import com.mongodb.MongoOptions; @@ -52,9 +49,12 @@ public class MongoOptionsFactoryBean implements FactoryBean, Initi private int writeTimeout = DEFAULT_MONGO_OPTIONS.getWtimeout(); private boolean writeFsync = DEFAULT_MONGO_OPTIONS.isFsync(); - private boolean autoConnectRetry = !isMongo3Driver() ? getAutoConnectRetry(DEFAULT_MONGO_OPTIONS) : false; - private long maxAutoConnectRetryTime = !isMongo3Driver() ? getMaxAutoConnectRetryTime(DEFAULT_MONGO_OPTIONS) : -1; - private boolean slaveOk = !isMongo3Driver() ? getSlaveOk(DEFAULT_MONGO_OPTIONS) : false; + private boolean autoConnectRetry = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker + .getAutoConnectRetry(DEFAULT_MONGO_OPTIONS) : false; + private long maxAutoConnectRetryTime = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker + .getMaxAutoConnectRetryTime(DEFAULT_MONGO_OPTIONS) : -1; + private boolean slaveOk = !MongoClientVersion.isMongo3Driver() ? ReflectiveMongoOptionsInvoker + .getSlaveOk(DEFAULT_MONGO_OPTIONS) : false; private boolean ssl; private SSLSocketFactory sslSocketFactory; @@ -233,7 +233,7 @@ public void afterPropertiesSet() { options.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault()); } - if (!isMongo3Driver()) { + if (!MongoClientVersion.isMongo3Driver()) { ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry); ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index e5c74f2013..96105e3ab1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -15,14 +15,8 @@ */ package org.springframework.data.mongodb.core; -import static com.mongodb.ReadPreference.*; -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.data.mongodb.ReflectiveDbInvoker.*; -import static org.springframework.data.mongodb.ReflectiveMapReduceInvoker.*; -import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.SerializationUtils.*; -import static org.springframework.util.ObjectUtils.*; import java.io.IOException; import java.util.ArrayList; @@ -65,6 +59,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.mongodb.MongoClientVersion; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; @@ -104,6 +99,7 @@ import org.springframework.jca.cci.core.ConnectionCallback; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -345,7 +341,8 @@ public CommandResult doInDB(DB db) throws MongoException, DataAccessException { */ @Deprecated public CommandResult executeCommand(final DBObject command, final int options) { - return executeCommand(command, (options & Bytes.QUERYOPTION_SLAVEOK) != 0 ? secondaryPreferred() : primary()); + return executeCommand(command, (options & Bytes.QUERYOPTION_SLAVEOK) != 0 ? ReadPreference.secondaryPreferred() + : ReadPreference.primary()); } /* @@ -444,10 +441,10 @@ public T executeInSession(final DbCallback action) { return execute(new DbCallback() { public T doInDB(DB db) throws MongoException, DataAccessException { try { - requestStart(db); + ReflectiveDbInvoker.requestStart(db); return action.doInDB(db); } finally { - requestDone(db); + ReflectiveDbInvoker.requestDone(db); } } }); @@ -736,7 +733,8 @@ protected WriteConcern prepareWriteConcern(MongoAction mongoAction) { WriteConcern wc = writeConcernResolver.resolve(mongoAction); - if (isMongo3Driver() && nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking) + if (MongoClientVersion.isMongo3Driver() + && ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking) && (wc == null || wc.getW() < 1)) { return WriteConcern.ACKNOWLEDGED; } @@ -1061,7 +1059,7 @@ public WriteResult doInCollection(DBCollection collection) throws MongoException : collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse); if (entity != null && entity.hasVersionProperty() && !multi) { - if (wasAcknowledged(writeResult) && writeResult.getN() == 0 + if (ReflectiveWriteResultInvoker.wasAcknowledged(writeResult) && writeResult.getN() == 0 && dbObjectContainsVersionProperty(queryObj, entity)) { throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity: " + updateObj.toMap().toString() + " to collection " + collectionName); @@ -1532,7 +1530,7 @@ private void copyMapReduceOptionsToCommand(Query query, MapReduceOptions mapRedu } if (!mapReduceOptions.getExtraOptions().isEmpty()) { for (Map.Entry entry : mapReduceOptions.getExtraOptions().entrySet()) { - addExtraOption(mapReduceCommand, entry.getKey(), entry.getValue()); + ReflectiveMapReduceInvoker.addExtraOption(mapReduceCommand, entry.getKey(), entry.getValue()); } } if (mapReduceOptions.getFinalizeFunction() != null) { @@ -1924,7 +1922,7 @@ protected void handleAnyWriteResultErrors(WriteResult writeResult, DBObject quer return; } - String error = getError(writeResult); + String error = ReflectiveWriteResultInvoker.getError(writeResult); if (error == null) { return; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDBCollectionInvoker.java similarity index 95% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDBCollectionInvoker.java index 8454889939..b814f60b94 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBCollectionInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDBCollectionInvoker.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; +package org.springframework.data.mongodb.core; import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.util.ReflectionUtils.*; import java.lang.reflect.Method; +import org.springframework.data.mongodb.MongoClientVersion; + import com.mongodb.DBCollection; import com.mongodb.DBObject; @@ -30,7 +32,7 @@ * @author Christoph Strobl * @since 1.7 */ -public class ReflectiveDBCollectionInvoker { +class ReflectiveDBCollectionInvoker { private static final Method GEN_INDEX_NAME_METHOD; private static final Method RESET_INDEX_CHACHE_METHOD; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDbInvoker.java similarity index 72% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDbInvoker.java index 40b7947eaf..600c365ad7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDbInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveDbInvoker.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; - -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.util.ReflectionUtils.*; +package org.springframework.data.mongodb.core; import java.lang.reflect.Method; import org.springframework.data.authentication.UserCredentials; +import org.springframework.data.mongodb.CannotGetMongoDbConnectionException; +import org.springframework.data.mongodb.MongoClientVersion; +import org.springframework.util.ReflectionUtils; import com.mongodb.DB; import com.mongodb.Mongo; @@ -32,7 +32,7 @@ * @author Christoph Strobl * @since 1.7 */ -public final class ReflectiveDbInvoker { +final class ReflectiveDbInvoker { private static final Method DB_IS_AUTHENTICATED_METHOD; private static final Method DB_AUTHENTICATE_METHOD; @@ -42,11 +42,11 @@ public final class ReflectiveDbInvoker { static { - DB_IS_AUTHENTICATED_METHOD = findMethod(DB.class, "isAuthenticated"); - DB_AUTHENTICATE_METHOD = findMethod(DB.class, "authenticate", String.class, char[].class); - DB_REQUEST_DONE_METHOD = findMethod(DB.class, "requestDone"); - DB_ADD_USER_METHOD = findMethod(DB.class, "addUser", String.class, char[].class); - DB_REQUEST_START_METHOD = findMethod(DB.class, "requestStart"); + DB_IS_AUTHENTICATED_METHOD = ReflectionUtils.findMethod(DB.class, "isAuthenticated"); + DB_AUTHENTICATE_METHOD = ReflectionUtils.findMethod(DB.class, "authenticate", String.class, char[].class); + DB_REQUEST_DONE_METHOD = ReflectionUtils.findMethod(DB.class, "requestDone"); + DB_ADD_USER_METHOD = ReflectionUtils.findMethod(DB.class, "addUser", String.class, char[].class); + DB_REQUEST_START_METHOD = ReflectionUtils.findMethod(DB.class, "requestStart"); } private ReflectiveDbInvoker() {} @@ -67,13 +67,13 @@ public static void authenticate(Mongo mongo, DB db, UserCredentials credentials, synchronized (authDb) { - Boolean isAuthenticated = (Boolean) invokeMethod(DB_IS_AUTHENTICATED_METHOD, authDb); + Boolean isAuthenticated = (Boolean) ReflectionUtils.invokeMethod(DB_IS_AUTHENTICATED_METHOD, authDb); if (!isAuthenticated) { String username = credentials.getUsername(); String password = credentials.hasPassword() ? credentials.getPassword() : null; - Boolean authenticated = (Boolean) invokeMethod(DB_AUTHENTICATE_METHOD, authDb, username, + Boolean authenticated = (Boolean) ReflectionUtils.invokeMethod(DB_AUTHENTICATE_METHOD, authDb, username, password == null ? null : password.toCharArray()); if (!authenticated) { throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName + "], " @@ -91,11 +91,11 @@ public static void authenticate(Mongo mongo, DB db, UserCredentials credentials, */ public static void requestStart(DB db) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } - invokeMethod(DB_REQUEST_START_METHOD, db); + ReflectionUtils.invokeMethod(DB_REQUEST_START_METHOD, db); } /** @@ -106,11 +106,11 @@ public static void requestStart(DB db) { */ public static void requestDone(DB db) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } - invokeMethod(DB_REQUEST_DONE_METHOD, db); + ReflectionUtils.invokeMethod(DB_REQUEST_DONE_METHOD, db); } /** @@ -125,7 +125,7 @@ public static void addUser(DB db, String username, char[] password) { throw new UnsupportedOperationException("Please DB.command to call either the createUser or updateUser command"); } - invokeMethod(DB_ADD_USER_METHOD, db, username, password); + ReflectionUtils.invokeMethod(DB_ADD_USER_METHOD, db, username, password); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMapReduceInvoker.java similarity index 71% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMapReduceInvoker.java index 3307bcad4a..72065909ed 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMapReduceInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMapReduceInvoker.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; - -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.util.Assert.*; -import static org.springframework.util.ReflectionUtils.*; +package org.springframework.data.mongodb.core; import java.lang.reflect.Method; +import org.springframework.data.mongodb.MongoClientVersion; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + import com.mongodb.MapReduceCommand; /** @@ -30,13 +30,14 @@ * @author Christoph Strobl * @since 1.7 */ -public final class ReflectiveMapReduceInvoker { +final class ReflectiveMapReduceInvoker { private static final Method ADD_EXTRA_OPTION_METHOD; static { - ADD_EXTRA_OPTION_METHOD = findMethod(MapReduceCommand.class, "addExtraOption", String.class, Object.class); + ADD_EXTRA_OPTION_METHOD = ReflectionUtils.findMethod(MapReduceCommand.class, "addExtraOption", String.class, + Object.class); } private ReflectiveMapReduceInvoker() {} @@ -50,12 +51,12 @@ private ReflectiveMapReduceInvoker() {} */ public static void addExtraOption(MapReduceCommand cmd, String key, Object value) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } - notNull(cmd, "MapReduceCommand must not be null!"); - invokeMethod(ADD_EXTRA_OPTION_METHOD, cmd, key, value); + Assert.notNull(cmd, "MapReduceCommand must not be null!"); + ReflectionUtils.invokeMethod(ADD_EXTRA_OPTION_METHOD, cmd, key, value); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvoker.java similarity index 75% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvoker.java index 698b0b38f6..f8f4f6b6d0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveMongoOptionsInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvoker.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; - -import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.util.ReflectionUtils.*; +package org.springframework.data.mongodb.core; import java.lang.reflect.Method; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.data.mongodb.MongoClientVersion; +import org.springframework.util.ReflectionUtils; import com.mongodb.MongoOptions; @@ -31,7 +30,7 @@ * @author Christoph Strobl * @since 1.7 */ -public class ReflectiveMongoOptionsInvoker { +class ReflectiveMongoOptionsInvoker { private static final Method GET_AUTO_CONNECT_RETRY_METHOD; private static final Method SET_AUTO_CONNECT_RETRY_METHOD; @@ -40,10 +39,13 @@ public class ReflectiveMongoOptionsInvoker { static { - SET_AUTO_CONNECT_RETRY_METHOD = findMethod(MongoOptions.class, "setAutoConnectRetry", boolean.class); - GET_AUTO_CONNECT_RETRY_METHOD = findMethod(MongoOptions.class, "isAutoConnectRetry"); - SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = findMethod(MongoOptions.class, "setMaxAutoConnectRetryTime", long.class); - GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = findMethod(MongoOptions.class, "getMaxAutoConnectRetryTime"); + SET_AUTO_CONNECT_RETRY_METHOD = ReflectionUtils + .findMethod(MongoOptions.class, "setAutoConnectRetry", boolean.class); + GET_AUTO_CONNECT_RETRY_METHOD = ReflectionUtils.findMethod(MongoOptions.class, "isAutoConnectRetry"); + SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = ReflectionUtils.findMethod(MongoOptions.class, + "setMaxAutoConnectRetryTime", long.class); + GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD = ReflectionUtils.findMethod(MongoOptions.class, + "getMaxAutoConnectRetryTime"); } private ReflectiveMongoOptionsInvoker() {} @@ -57,10 +59,10 @@ private ReflectiveMongoOptionsInvoker() {} */ public static void setAutoConnectRetry(MongoOptions options, boolean autoConnectRetry) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } - invokeMethod(SET_AUTO_CONNECT_RETRY_METHOD, options, autoConnectRetry); + ReflectionUtils.invokeMethod(SET_AUTO_CONNECT_RETRY_METHOD, options, autoConnectRetry); } /** @@ -72,10 +74,10 @@ public static void setAutoConnectRetry(MongoOptions options, boolean autoConnect */ public static void setMaxAutoConnectRetryTime(MongoOptions options, long maxAutoConnectRetryTime) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } - invokeMethod(SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options, maxAutoConnectRetryTime); + ReflectionUtils.invokeMethod(SET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options, maxAutoConnectRetryTime); } /** @@ -87,7 +89,7 @@ public static void setMaxAutoConnectRetryTime(MongoOptions options, long maxAuto */ public static void setSlaveOk(MongoOptions options, boolean slaveOk) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return; } new DirectFieldAccessor(options).setPropertyValue("slaveOk", slaveOk); @@ -103,7 +105,7 @@ public static void setSlaveOk(MongoOptions options, boolean slaveOk) { */ public static boolean getSlaveOk(MongoOptions options) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { throw new UnsupportedOperationException( "Cannot get value for autoConnectRetry which has been removed in mongo-java-driver version 3."); } @@ -120,11 +122,11 @@ public static boolean getSlaveOk(MongoOptions options) { */ public static boolean getAutoConnectRetry(MongoOptions options) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { throw new UnsupportedOperationException( "Cannot get value for autoConnectRetry which has been removed in mongo-java-driver version 3."); } - return ((Boolean) invokeMethod(GET_AUTO_CONNECT_RETRY_METHOD, options)).booleanValue(); + return ((Boolean) ReflectionUtils.invokeMethod(GET_AUTO_CONNECT_RETRY_METHOD, options)).booleanValue(); } /** @@ -137,11 +139,11 @@ public static boolean getAutoConnectRetry(MongoOptions options) { */ public static long getMaxAutoConnectRetryTime(MongoOptions options) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { throw new UnsupportedOperationException( "Cannot get value for maxAutoConnectRetryTime which has been removed in mongo-java-driver version 3."); } - return ((Long) invokeMethod(GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options)).longValue(); + return ((Long) ReflectionUtils.invokeMethod(GET_MAX_AUTO_CONNECT_RETRY_TIME_METHOD, options)).longValue(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteConcernInvoker.java similarity index 78% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteConcernInvoker.java index 72965603c6..8f3be853b7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteConcernInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteConcernInvoker.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; - -import static org.springframework.data.mongodb.MongoClientVersion.*; +package org.springframework.data.mongodb.core; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.data.mongodb.MongoClientVersion; import com.mongodb.WriteConcern; @@ -28,14 +27,14 @@ * @author Christoph Strobl * @since 1.7 */ -public class ReflectiveWriteConcernInvoker { +class ReflectiveWriteConcernInvoker { private static final WriteConcern NONE_OR_UNACKNOWLEDGED; static { - NONE_OR_UNACKNOWLEDGED = isMongo3Driver() ? WriteConcern.UNACKNOWLEDGED : (WriteConcern) new DirectFieldAccessor( - new WriteConcern()).getPropertyValue("NONE"); + NONE_OR_UNACKNOWLEDGED = MongoClientVersion.isMongo3Driver() ? WriteConcern.UNACKNOWLEDGED + : (WriteConcern) new DirectFieldAccessor(new WriteConcern()).getPropertyValue("NONE"); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteResultInvoker.java similarity index 81% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteResultInvoker.java index e9ac6373b0..224e649e0e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveWriteResultInvoker.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReflectiveWriteResultInvoker.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; +package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.util.ReflectionUtils.*; import java.lang.reflect.Method; +import org.springframework.data.mongodb.MongoClientVersion; +import org.springframework.util.ReflectionUtils; + import com.mongodb.MongoException; import com.mongodb.WriteResult; @@ -30,7 +32,7 @@ * @author Christoph Strobl * @since 1.7 */ -public final class ReflectiveWriteResultInvoker { +final class ReflectiveWriteResultInvoker { private static final Method GET_ERROR_METHOD; private static final Method WAS_ACKNOWLEDGED_METHOD; @@ -49,7 +51,7 @@ private ReflectiveWriteResultInvoker() {} */ public static String getError(WriteResult writeResult) { - if (isMongo3Driver()) { + if (MongoClientVersion.isMongo3Driver()) { return null; } @@ -61,7 +63,8 @@ public static String getError(WriteResult writeResult) { * @return return in case of mongo-java-driver version 2. */ public static boolean wasAcknowledged(WriteResult writeResult) { - return isMongo3Driver() ? ((Boolean) invokeMethod(WAS_ACKNOWLEDGED_METHOD, writeResult)).booleanValue() : true; + return MongoClientVersion.isMongo3Driver() ? ((Boolean) ReflectionUtils.invokeMethod(WAS_ACKNOWLEDGED_METHOD, + writeResult)).booleanValue() : true; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java index 8772f853ac..0ca28a29ac 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java @@ -15,8 +15,6 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.util.Assert.*; - import java.net.UnknownHostException; import org.springframework.beans.factory.DisposableBean; @@ -24,6 +22,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import com.mongodb.DB; @@ -133,9 +132,10 @@ public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) { private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials, boolean mongoInstanceCreated, String authenticationDatabaseName) { - notNull(mongo, "Mongo must not be null"); - hasText(databaseName, "Database name must not be empty"); - isTrue(databaseName.matches("[\\w-]+"), "Database name must only contain letters, numbers, underscores and dashes!"); + Assert.notNull(mongo, "Mongo must not be null"); + Assert.hasText(databaseName, "Database name must not be empty"); + Assert.isTrue(databaseName.matches("[\\w-]+"), + "Database name must only contain letters, numbers, underscores and dashes!"); this.mongo = mongo; this.databaseName = databaseName; @@ -145,7 +145,7 @@ private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials c this.authenticationDatabaseName = StringUtils.hasText(authenticationDatabaseName) ? authenticationDatabaseName : databaseName; - isTrue(this.authenticationDatabaseName.matches("[\\w-]+"), + Assert.isTrue(this.authenticationDatabaseName.matches("[\\w-]+"), "Authentication database name must only contain letters, numbers, underscores and dashes!"); } @@ -157,8 +157,8 @@ private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials c */ private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) { - notNull(client, "MongoClient must not be null!"); - hasText(databaseName, "Database name must not be empty!"); + Assert.notNull(client, "MongoClient must not be null!"); + Assert.hasText(databaseName, "Database name must not be empty!"); this.mongo = client; this.databaseName = databaseName; @@ -191,7 +191,7 @@ public DB getDb() throws DataAccessException { */ public DB getDb(String dbName) throws DataAccessException { - hasText(dbName, "Database name must not be empty."); + Assert.hasText(dbName, "Database name must not be empty."); DB db = MongoDbUtils.getDB(mongo, dbName, credentials, authenticationDatabaseName); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java index 941795df9d..ddea7ff5c9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java @@ -34,7 +34,6 @@ import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.LazyLoadingException; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.data.mongodb.ReflectiveDBRefResolver; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.objenesis.ObjenesisStd; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReflectiveDBRefResolver.java similarity index 81% rename from spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java rename to spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReflectiveDBRefResolver.java index 8b3515d5e2..8b83275153 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReflectiveDBRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReflectiveDBRefResolver.java @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.mongodb; +package org.springframework.data.mongodb.core.convert; -import static org.springframework.util.Assert.*; import static org.springframework.util.ReflectionUtils.*; import java.lang.reflect.Method; +import org.springframework.data.mongodb.MongoClientVersion; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBObject; @@ -32,7 +35,7 @@ * @author Christoph Strobl * @since 1.7 */ -public class ReflectiveDBRefResolver { +class ReflectiveDBRefResolver { private static final Method FETCH_METHOD; @@ -51,13 +54,13 @@ public class ReflectiveDBRefResolver { */ public static DBObject fetch(DB db, DBRef ref) { - notNull(ref, "DBRef to fetch must not be null!"); + Assert.notNull(ref, "DBRef to fetch must not be null!"); if (MongoClientVersion.isMongo3Driver()) { return db.getCollection(ref.getCollectionName()).findOne(ref.getId()); } - return (DBObject) invokeMethod(FETCH_METHOD, ref); + return (DBObject) ReflectionUtils.invokeMethod(FETCH_METHOD, ref); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java index f2ad71bc75..8bb2e00006 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java @@ -15,11 +15,11 @@ */ package org.springframework.data.mongodb.core.mapreduce; -import static org.springframework.util.Assert.*; - import java.util.Iterator; import java.util.List; +import org.springframework.util.Assert; + import com.mongodb.DBObject; import com.mongodb.MapReduceOutput; @@ -49,8 +49,8 @@ public class MapReduceResults implements Iterable { @Deprecated public MapReduceResults(List mappedResults, DBObject rawResults) { - notNull(mappedResults); - notNull(rawResults); + Assert.notNull(mappedResults); + Assert.notNull(rawResults); this.mappedResults = mappedResults; this.rawResults = rawResults; @@ -68,8 +68,8 @@ public MapReduceResults(List mappedResults, DBObject rawResults) { */ public MapReduceResults(List mappedResults, MapReduceOutput mapReduceOutput) { - notNull(mappedResults, "MappedResults must not be null!"); - notNull(mapReduceOutput, "MapReduceOutput must not be null!"); + Assert.notNull(mappedResults, "MappedResults must not be null!"); + Assert.notNull(mapReduceOutput, "MapReduceOutput must not be null!"); this.mappedResults = mappedResults; this.rawResults = null; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java index 1e836c24db..4e94919bf3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java @@ -19,7 +19,6 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; import org.junit.Before; import org.junit.Test; @@ -34,6 +33,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.ReflectiveMongoOptionsInvokerTestUtil; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; import org.springframework.test.util.ReflectionTestUtils; @@ -140,7 +140,7 @@ public void parsesMaxAutoConnectRetryTimeCorrectly() { reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml")); Mongo mongo = factory.getBean(Mongo.class); - assertThat(getMaxAutoConnectRetryTime(mongo.getMongoOptions()), is(27L)); + assertThat(ReflectiveMongoOptionsInvokerTestUtil.getMaxAutoConnectRetryTime(mongo.getMongoOptions()), is(27L)); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java index b63c08e7f2..0f79727fe6 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java @@ -27,9 +27,9 @@ import org.springframework.context.ApplicationContext; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker; import org.springframework.data.mongodb.core.MongoFactoryBean; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.ReflectiveMongoOptionsInvokerTestUtil; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.gridfs.GridFsOperations; import org.springframework.test.context.ContextConfiguration; @@ -253,8 +253,8 @@ public void testMongoSingletonWithPropertyPlaceHolders() throws Exception { if (!isMongo3Driver()) { assertEquals(true, mongoOpts.fsync); - assertEquals(true, ReflectiveMongoOptionsInvoker.getAutoConnectRetry(mongoOpts)); - assertEquals(true, ReflectiveMongoOptionsInvoker.getSlaveOk(mongoOpts)); + assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getAutoConnectRetry(mongoOpts)); + assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getSlaveOk(mongoOpts)); } else { assertEquals(false, mongoOpts.fsync); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java index 7043a72a6a..744387de80 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java @@ -17,7 +17,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import static org.springframework.data.mongodb.ReflectiveDBCollectionInvoker.*; +import static org.springframework.data.mongodb.core.ReflectiveDBCollectionInvoker.*; import org.junit.Before; import org.junit.Test; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java index 88654971a4..57565d461f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsIntegrationTests.java @@ -30,7 +30,6 @@ import org.junit.Test; import org.springframework.dao.DataAccessException; import org.springframework.data.authentication.UserCredentials; -import org.springframework.data.mongodb.ReflectiveDbInvoker; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import com.mongodb.DB; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java index 4d3f73b9e7..61643bba60 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java @@ -19,7 +19,7 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.springframework.data.mongodb.MongoClientVersion.*; -import static org.springframework.data.mongodb.ReflectiveMongoOptionsInvoker.*; +import static org.springframework.data.mongodb.core.ReflectiveMongoOptionsInvoker.*; import javax.net.ssl.SSLSocketFactory; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 337c6df051..4352d07ba0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -18,8 +18,8 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.*; -import static org.springframework.data.mongodb.ReflectiveWriteConcernInvoker.*; -import static org.springframework.data.mongodb.ReflectiveWriteResultInvoker.*; +import static org.springframework.data.mongodb.core.ReflectiveWriteConcernInvoker.*; +import static org.springframework.data.mongodb.core.ReflectiveWriteResultInvoker.*; 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.*; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvokerTestUtil.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvokerTestUtil.java new file mode 100644 index 0000000000..4bcb185ed7 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReflectiveMongoOptionsInvokerTestUtil.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.core; + +import com.mongodb.MongoOptions; + +/** + * Helper class allowing to keep {@link ReflectiveMongoOptionsInvoker} within default visibility while using it publicly + * across tests. + * + * @author Christoph Strobl + */ +public class ReflectiveMongoOptionsInvokerTestUtil { + + public static void setAutoConnectRetry(MongoOptions options, boolean autoConnectRetry) { + ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry); + } + + public static void setMaxAutoConnectRetryTime(MongoOptions options, long maxAutoConnectRetryTime) { + ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime); + } + + public static void setSlaveOk(MongoOptions options, boolean slaveOk) { + ReflectiveMongoOptionsInvoker.setSlaveOk(options, slaveOk); + } + + public static boolean getSlaveOk(MongoOptions options) { + return ReflectiveMongoOptionsInvoker.getSlaveOk(options); + } + + public static boolean getAutoConnectRetry(MongoOptions options) { + return ReflectiveMongoOptionsInvoker.getAutoConnectRetry(options); + } + + public static long getMaxAutoConnectRetryTime(MongoOptions options) { + return ReflectiveMongoOptionsInvoker.getMaxAutoConnectRetryTime(options); + } + +} diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index c94ad0dba2..f52b3eac54 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -31,6 +31,7 @@ include::reference/mapping.adoc[] include::reference/cross-store.adoc[] include::reference/logging.adoc[] include::reference/jmx.adoc[] +include::reference/mongo-3.adoc[] :leveloffset: -1 [[appendix]] diff --git a/src/main/asciidoc/reference/mongo-3.adoc b/src/main/asciidoc/reference/mongo-3.adoc new file mode 100644 index 0000000000..0f7f6f57e1 --- /dev/null +++ b/src/main/asciidoc/reference/mongo-3.adoc @@ -0,0 +1,59 @@ +[[mongo.mongo-3]] += MongoDB 3.0 Support + +Spring Data MongoDB allows usage of both _mongo-java-driver_ generations 2 and 3 when connecting to a MongoDB 2.6/3.0 server running _MMap.v1_ or a MongoDB server 3.0 using _MMap.v1_ or the _WiredTiger_ storage engine. + +NOTE: Please refer to the driver and database specific documentation for major differences between those. + +NOTE: Operations that are no longer valid using a 3.x mongo-java-driver have been deprecated within Spring Data and will be removed in a subsequent release. + +[[mongodb:mongo-3-configuration]] +== Using Spring Data MongoDB with MongoDB 3.0 + +=== Configuration Options + +Some of the configuration options have been changed / removed for the _mongo-java-driver_. The following options will be ignored using the generation 3 driver: + + * autoConnectRetry + * maxAutoConnectRetryTime + * slaveOk + +=== WriteConcern and WriteConcernChecking + +The `WriteConcern.NONE`, which had been used as default by Spring Data MongoDB, was removed in 3.0. Therefore in a MongoDB 3 environment the `WriteConcern` will be defaulted to `WriteConcern.UNACKNOWLEGED`. In case `WriteResultChecking.EXCEPTION` is enabled the `WriteConcern` will be altered to `WriteConcern.ACKNOWLEDGED` for write operations, as otherwise errors during execution would not be throw correctly, since simply not raised by the driver. + +=== Authentication + +MongoDB Server generation 3 changed the authentication model when connecting to the DB. Therefore some of the configuration options available for authentication are no longer valid. Please use the `MongoClient` specific options for setting credentials via `MongoCredential` to provide authentication data. + +[source,java] +---- +@Configuration +public class ApplicationContextEventTestsAppConfig extends AbstractMongoConfiguration { + + @Override + public String getDatabaseName() { + return "database"; + } + + @Override + @Bean + public Mongo mongo() throws Exception { + return new MongoClient(singletonList(new ServerAddress("127.0.0.1", 27017)), + singletonList(MongoCredential.createCredential("name", "db", "pwd".toCharArray()))); + } +} +---- + +=== Other things to be aware of + +This section covers additional things to keep in mind when using the 3.0 driver. + +* `IndexOperations.resetIndexCache()` is no longer supported. +* Any `MapReduceOptions.extraOption` is silently ignored. +* `WriteResult` does not longer hold error informations but throws an Exception. +* `MongoOperations.executeInSession()` no longer calls `requestStart` / `requestDone`. +* Index name generation has become a driver internal operations, still we use the 2.x schema to generate names. +* Some Exception messages differ between the generation 2 and 3 servers as well as between _MMap.v1_ and _WiredTiger_ storage engine. + + From 7f83ac468c0b3f90e2c61f551f8176da89af5305 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 25 Feb 2015 09:19:53 +0100 Subject: [PATCH 6/8] DATAMONGO-1158 - Add explicit config options for MongoClient. We added an explicit mongo-client element and client-options to the configuration schema. These elements will replace existing mongo and options elements in a subsequent release. Next we need to allow passing in credentials, so one is able to configure authentication for mongo-client. --- .../mongodb/config/MongoClientParser.java | 81 ++ .../mongodb/config/MongoNamespaceHandler.java | 4 +- .../data/mongodb/config/MongoParser.java | 25 +- .../mongodb/config/MongoParsingUtils.java | 79 +- .../config/ReadPreferencePropertyEditor.java | 61 ++ .../mongodb/core/MongoClientFactoryBean.java | 186 ++++ .../core/MongoClientOptionsFactoryBean.java | 309 +++++++ .../data/mongodb/core/MongoFactoryBean.java | 39 +- .../main/resources/META-INF/spring.schemas | 3 +- .../data/mongodb/config/spring-mongo-1.7.xsd | 868 ++++++++++++++++++ .../MongoClientParserIntegrationTests.java | 105 +++ ...ReadPreferencePropertyEditorUnitTests.java | 75 ++ ...entOptionsFactoryBeanIntegrationTests.java | 52 ++ .../resources/namespace/mongoClient-bean.xml | 16 + src/main/asciidoc/reference/mongo-3.adoc | 19 + 15 files changed, 1862 insertions(+), 60 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBean.java create mode 100644 spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBeanIntegrationTests.java create mode 100644 spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java new file mode 100644 index 0000000000..c95c31ba1f --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.parsing.CompositeComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.config.BeanComponentDefinitionBuilder; +import org.springframework.data.config.ParsingUtils; +import org.springframework.data.mongodb.core.MongoClientFactoryBean; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for {@code mongo-client} definitions. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class MongoClientParser implements BeanDefinitionParser { + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) + */ + public BeanDefinition parse(Element element, ParserContext parserContext) { + + Object source = parserContext.extractSource(element); + String id = element.getAttribute("id"); + + BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoClientFactoryBean.class); + + ParsingUtils.setPropertyValue(builder, element, "port", "port"); + ParsingUtils.setPropertyValue(builder, element, "host", "host"); + + MongoParsingUtils.parseMongoClientOptions(element, builder); + MongoParsingUtils.parseReplicaSet(element, builder); + + String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO_BEAN_NAME; + + parserContext.pushContainingComponent(new CompositeComponentDefinition("Mongo", source)); + + BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId); + parserContext.registerBeanComponent(mongoComponent); + + BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils + .getServerAddressPropertyEditorBuilder()); + parserContext.registerBeanComponent(serverAddressPropertyEditor); + + BeanComponentDefinition writeConcernEditor = helper.getComponent(MongoParsingUtils + .getWriteConcernPropertyEditorBuilder()); + parserContext.registerBeanComponent(writeConcernEditor); + + BeanComponentDefinition readPreferenceEditor = helper.getComponent(MongoParsingUtils + .getReadPreferencePropertyEditorBuilder()); + parserContext.registerBeanComponent(readPreferenceEditor); + + parserContext.popAndRegisterContainingComponent(); + + return mongoComponent.getBeanDefinition(); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java index 89b6c2f843..377eedb446 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -22,6 +22,7 @@ * * @author Oliver Gierke * @author Martin Baumgartner + * @author Christoph Strobl */ public class MongoNamespaceHandler extends NamespaceHandlerSupport { @@ -33,6 +34,7 @@ public void init() { registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser()); registerBeanDefinitionParser("mongo", new MongoParser()); + registerBeanDefinitionParser("mongo-client", new MongoClientParser()); registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser()); registerBeanDefinitionParser("jmx", new MongoJmxParser()); registerBeanDefinitionParser("auditing", new MongoAuditingBeanDefinitionParser()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java index ca605a02e2..604214f261 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2015 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. @@ -15,14 +15,10 @@ */ package org.springframework.data.mongodb.config; -import java.util.Map; - import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.CustomEditorConfigurer; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.config.BeanComponentDefinitionBuilder; @@ -36,6 +32,7 @@ * * @author Mark Pollack * @author Oliver Gierke + * @author Christoph Strobl */ public class MongoParser implements BeanDefinitionParser { @@ -64,7 +61,8 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId); parserContext.registerBeanComponent(mongoComponent); - BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(registerServerAddressPropertyEditor()); + BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils + .getServerAddressPropertyEditorBuilder()); parserContext.registerBeanComponent(serverAddressPropertyEditor); BeanComponentDefinition writeConcernPropertyEditor = helper.getComponent(MongoParsingUtils .getWriteConcernPropertyEditorBuilder()); @@ -75,19 +73,4 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { return mongoComponent.getBeanDefinition(); } - /** - * One should only register one bean definition but want to have the convenience of using - * AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the - * container. - */ - private BeanDefinitionBuilder registerServerAddressPropertyEditor() { - - Map customEditors = new ManagedMap(); - customEditors.put("com.mongodb.ServerAddress[]", - "org.springframework.data.mongodb.config.ServerAddressPropertyEditor"); - - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); - builder.addPropertyValue("customEditors", customEditors); - return builder; - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index b69443012c..9f71aa4ce9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2015 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. @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.data.mongodb.core.MongoClientOptionsFactoryBean; import org.springframework.data.mongodb.core.MongoOptionsFactoryBean; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; @@ -34,6 +35,7 @@ * @author Mark Pollack * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ abstract class MongoParsingUtils { @@ -87,6 +89,47 @@ static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBui return true; } + /** + * @param element + * @param mongoClientBuilder + * @return + * @since 1.7 + */ + public static boolean parseMongoClientOptions(Element element, BeanDefinitionBuilder mongoClientBuilder) { + + Element optionsElement = DomUtils.getChildElementByTagName(element, "client-options"); + if (optionsElement == null) { + return false; + } + + BeanDefinitionBuilder clientOptionsDefBuilder = BeanDefinitionBuilder + .genericBeanDefinition(MongoClientOptionsFactoryBean.class); + + setPropertyValue(clientOptionsDefBuilder, optionsElement, "description", "description"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-connections-per-host", "minConnectionsPerHost"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier", + "threadsAllowedToBlockForConnectionMultiplier"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-idle-time", "maxConnectionIdleTime"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-life-time", "maxConnectionLifeTime"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "read-preference", "readPreference"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "write-concern", "writeConcern"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-frequency", "heartbeatFrequency"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-heartbeat-frequency", "minHeartbeatFrequency"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-connect-timeout", "heartbeatConnectTimeout"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-socket-timeout", "heartbeatSocketTimeout"); + setPropertyValue(clientOptionsDefBuilder, optionsElement, "ssl", "ssl"); + setPropertyReference(clientOptionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory"); + + mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition()); + + return true; + } + /** * Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a * {@link WriteConcernPropertyEditor}. @@ -103,4 +146,38 @@ static BeanDefinitionBuilder getWriteConcernPropertyEditorBuilder() { return builder; } + + /** + * Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a + * {@link ReadPreferencePropertyEditor}. + * + * @return + * @since 1.7 + */ + static BeanDefinitionBuilder getReadPreferencePropertyEditorBuilder() { + + Map> customEditors = new ManagedMap>(); + customEditors.put("com.mongodb.ReadPreference", ReadPreferencePropertyEditor.class); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); + builder.addPropertyValue("customEditors", customEditors); + + return builder; + } + + /** + * One should only register one bean definition but want to have the convenience of using + * AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the + * container. + */ + static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() { + + Map customEditors = new ManagedMap(); + customEditors.put("com.mongodb.ServerAddress[]", + "org.springframework.data.mongodb.config.ServerAddressPropertyEditor"); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); + builder.addPropertyValue("customEditors", customEditors); + return builder; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java new file mode 100644 index 0000000000..9a6fa89587 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import java.beans.PropertyEditorSupport; + +import com.mongodb.ReadPreference; + +/** + * Parse a {@link String} to a {@link ReadPreference}. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class ReadPreferencePropertyEditor extends PropertyEditorSupport { + + @Override + public void setAsText(String readPreferenceString) throws IllegalArgumentException { + + if (readPreferenceString == null) { + return; + } + + ReadPreference preference = null; + try { + preference = ReadPreference.valueOf(readPreferenceString); + } catch (IllegalArgumentException ex) { + // ignore this one and try to map it differently + } + + if (preference != null) { + setValue(preference); + } else if ("PRIMARY".equalsIgnoreCase(readPreferenceString)) { + setValue(ReadPreference.primary()); + } else if ("PRIMARY_PREFERRED".equalsIgnoreCase(readPreferenceString)) { + setValue(ReadPreference.primaryPreferred()); + } else if ("SECONDARY".equalsIgnoreCase(readPreferenceString)) { + setValue(ReadPreference.secondary()); + } else if ("SECONDARY_PREFERRED".equalsIgnoreCase(readPreferenceString)) { + setValue(ReadPreference.secondaryPreferred()); + } else if ("NEAREST".equalsIgnoreCase(readPreferenceString)) { + setValue(ReadPreference.nearest()); + } else { + throw new IllegalArgumentException(String.format("Cannot find matching ReadPreference for %s", + readPreferenceString)); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java new file mode 100644 index 0000000000..4794b90a40 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java @@ -0,0 +1,186 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.core; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; + +/** + * Convenient factory for configuring MongoDB. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class MongoClientFactoryBean implements FactoryBean, InitializingBean, DisposableBean, + PersistenceExceptionTranslator { + + private MongoClient mongo; + + private MongoClientOptions mongoClientOptions; + + private String host; + private Integer port; + private List replicaSetSeeds; + private List credentials; + + private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); + + /** + * Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}. + * + * @param mongoClientOptions + */ + public void setMongoClientOptions(MongoClientOptions mongoClientOptions) { + this.mongoClientOptions = mongoClientOptions; + } + + /** + * Set the list of credentials to be used when creating {@link MongoClient}. + * + * @param credentials can be {@literal null}. + */ + public void setCredentials(List credentials) { + this.credentials = credentials; + } + + public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) { + this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds); + } + + /** + * @param elements the elements to filter + * @return a new unmodifiable {@link List#} from the given elements without nulls + */ + private List filterNonNullElementsAsList(T[] elements) { + + if (elements == null) { + return Collections.emptyList(); + } + + List candidateElements = new ArrayList(); + + for (T element : elements) { + if (element != null) { + candidateElements.add(element); + } + } + + return Collections.unmodifiableList(candidateElements); + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + /** + * @param exceptionTranslator + */ + public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public Mongo getObject() throws Exception { + return mongo; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return Mongo.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + /* + * (non-Javadoc) + * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) + */ + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return exceptionTranslator.translateExceptionIfPossible(ex); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + + if (mongoClientOptions == null) { + mongoClientOptions = MongoClientOptions.builder().build(); + } + if (credentials == null) { + credentials = Collections.emptyList(); + } + + this.mongo = createMongoClient(); + } + + private MongoClient createMongoClient() throws UnknownHostException { + + if (!CollectionUtils.isEmpty(replicaSetSeeds)) { + return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions); + } + + return new MongoClient(createConfiguredOrDefaultServerAddress(), credentials, mongoClientOptions); + } + + private ServerAddress createConfiguredOrDefaultServerAddress() throws UnknownHostException { + + ServerAddress defaultAddress = new ServerAddress(); + return new ServerAddress(StringUtils.hasText(host) ? host : defaultAddress.getHost(), + port != null ? port.intValue() : defaultAddress.getPort()); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.DisposableBean#destroy() + */ + public void destroy() throws Exception { + this.mongo.close(); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBean.java new file mode 100644 index 0000000000..8856815e1d --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBean.java @@ -0,0 +1,309 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.core; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.mongodb.MongoDbFactory; + +import com.mongodb.DBDecoderFactory; +import com.mongodb.DBEncoderFactory; +import com.mongodb.MongoClientOptions; +import com.mongodb.ReadPreference; +import com.mongodb.WriteConcern; + +/** + * A factory bean for construction of a {@link MongoClientOptions} instance. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class MongoClientOptionsFactoryBean implements FactoryBean, InitializingBean { + + private static final MongoClientOptions DEFAULT_MONGO_OPTIONS = MongoClientOptions.builder().build(); + + private String description = DEFAULT_MONGO_OPTIONS.getDescription(); + private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost(); + private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost(); + private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS + .getThreadsAllowedToBlockForConnectionMultiplier(); + private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime(); + private int maxConnectionIdleTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionIdleTime(); + 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 ReadPreference readPreference = DEFAULT_MONGO_OPTIONS.getReadPreference(); + private DBDecoderFactory dbDecoderFactory = DEFAULT_MONGO_OPTIONS.getDbDecoderFactory(); + private DBEncoderFactory dbEncoderFactory = DEFAULT_MONGO_OPTIONS.getDbEncoderFactory(); + private WriteConcern writeConcern = DEFAULT_MONGO_OPTIONS.getWriteConcern(); + private SocketFactory socketFactory = DEFAULT_MONGO_OPTIONS.getSocketFactory(); + private boolean cursorFinalizerEnabled = DEFAULT_MONGO_OPTIONS.isCursorFinalizerEnabled(); + private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans(); + private int heartbeatFrequency = DEFAULT_MONGO_OPTIONS.getHeartbeatFrequency(); + private int minHeartbeatFrequency = DEFAULT_MONGO_OPTIONS.getMinHeartbeatFrequency(); + private int heartbeatConnectTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatConnectTimeout(); + private int heartbeatSocketTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatSocketTimeout(); + private String requiredReplicaSetName = DEFAULT_MONGO_OPTIONS.getRequiredReplicaSetName(); + + private boolean ssl; + private SSLSocketFactory sslSocketFactory; + + private MongoClientOptions clientOptions; + + /** + * Set the MongoClient description. + * + * @param description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Set the minimum number of connections per host. + * + * @param minConnectionsPerHost + */ + public void setMinConnectionsPerHost(int minConnectionsPerHost) { + this.minConnectionsPerHost = minConnectionsPerHost; + } + + /** + * Set the number of connections allowed per host. Will block if run out. Default is 10. System property + * {@code MONGO.POOLSIZE} can override + * + * @param connectionsPerHost + */ + public void setConnectionsPerHost(int connectionsPerHost) { + this.connectionsPerHost = connectionsPerHost; + } + + /** + * Set the multiplier for connectionsPerHost for # of threads that can block. Default is 5. If connectionsPerHost is + * 10, and threadsAllowedToBlockForConnectionMultiplier is 5, then 50 threads can block more than that and an + * exception will be thrown. + * + * @param threadsAllowedToBlockForConnectionMultiplier + */ + public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) { + this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier; + } + + /** + * Set the max wait time of a blocking thread for a connection. Default is 12000 ms (2 minutes) + * + * @param maxWaitTime + */ + public void setMaxWaitTime(int maxWaitTime) { + this.maxWaitTime = maxWaitTime; + } + + /** + * The maximum idle time for a pooled connection. + * + * @param maxConnectionIdleTime + */ + public void setMaxConnectionIdleTime(int maxConnectionIdleTime) { + this.maxConnectionIdleTime = maxConnectionIdleTime; + } + + /** + * Set the maximum life time for a pooled connection. + * + * @param maxConnectionLifeTime + */ + public void setMaxConnectionLifeTime(int maxConnectionLifeTime) { + this.maxConnectionLifeTime = maxConnectionLifeTime; + } + + /** + * Set the connect timeout in milliseconds. 0 is default and infinite. + * + * @param connectTimeout + */ + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + /** + * Set the socket timeout. 0 is default and infinite. + * + * @param socketTimeout + */ + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + /** + * Set the keep alive flag, controls whether or not to have socket keep alive timeout. Defaults to false. + * + * @param socketKeepAlive + */ + public void setSocketKeepAlive(boolean socketKeepAlive) { + this.socketKeepAlive = socketKeepAlive; + } + + /** + * Set the {@link ReadPreference}. + * + * @param readPreference + */ + public void setReadPreference(ReadPreference readPreference) { + this.readPreference = readPreference; + } + + /** + * Set the {@link WriteConcern} that will be the default value used when asking the {@link MongoDbFactory} for a DB + * object. + * + * @param writeConcern + */ + public void setWriteConcern(WriteConcern writeConcern) { + this.writeConcern = writeConcern; + } + + /** + * @param socketFactory + */ + public void setSocketFactory(SocketFactory socketFactory) { + this.socketFactory = socketFactory; + } + + /** + * Set the frequency that the driver will attempt to determine the current state of each server in the cluster. + * + * @param heartbeatFrequency + */ + public void setHeartbeatFrequency(int heartbeatFrequency) { + this.heartbeatFrequency = heartbeatFrequency; + } + + /** + * In the event that the driver has to frequently re-check a server's availability, it will wait at least this long + * since the previous check to avoid wasted effort. + * + * @param minHeartbeatFrequency + */ + public void setMinHeartbeatFrequency(int minHeartbeatFrequency) { + this.minHeartbeatFrequency = minHeartbeatFrequency; + } + + /** + * Set the connect timeout for connections used for the cluster heartbeat. + * + * @param heartbeatConnectTimeout + */ + public void setHeartbeatConnectTimeout(int heartbeatConnectTimeout) { + this.heartbeatConnectTimeout = heartbeatConnectTimeout; + } + + /** + * Set the socket timeout for connections used for the cluster heartbeat. + * + * @param heartbeatSocketTimeout + */ + public void setHeartbeatSocketTimeout(int heartbeatSocketTimeout) { + this.heartbeatSocketTimeout = heartbeatSocketTimeout; + } + + /** + * @param requiredReplicaSetName + */ + public void setRequiredReplicaSetName(String requiredReplicaSetName) { + this.requiredReplicaSetName = requiredReplicaSetName; + } + + /** + * This controls if the driver should us an SSL connection. Defaults to false. + * + * @param ssl + */ + public void setSsl(boolean ssl) { + this.ssl = ssl; + } + + /** + * Set the {@link SSLSocketFactory} to use for the {@literal SSL} connection. If none is configured here, + * {@link SSLSocketFactory#getDefault()} will be used. + * + * @param sslSocketFactory + */ + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() { + + SocketFactory socketFactoryToUse = ssl ? (sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory + .getDefault()) : this.socketFactory; + + this.clientOptions = MongoClientOptions.builder() // + .alwaysUseMBeans(this.alwaysUseMBeans) // + .connectionsPerHost(this.connectionsPerHost) // + .connectTimeout(connectTimeout) // + .cursorFinalizerEnabled(cursorFinalizerEnabled) // + .dbDecoderFactory(dbDecoderFactory) // + .dbEncoderFactory(dbEncoderFactory) // + .description(description) // + .heartbeatConnectTimeout(heartbeatConnectTimeout) // + .heartbeatFrequency(heartbeatFrequency) // + .heartbeatSocketTimeout(heartbeatSocketTimeout) // + .maxConnectionIdleTime(maxConnectionIdleTime) // + .maxConnectionLifeTime(maxConnectionLifeTime) // + .maxWaitTime(maxWaitTime) // + .minConnectionsPerHost(minConnectionsPerHost) // + .minHeartbeatFrequency(minHeartbeatFrequency) // + .readPreference(readPreference) // + .requiredReplicaSetName(requiredReplicaSetName) // + .socketFactory(socketFactoryToUse) // + .socketKeepAlive(socketKeepAlive) // + .socketTimeout(socketTimeout) // + .threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) // + .writeConcern(writeConcern).build(); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public MongoClientOptions getObject() { + return this.clientOptions; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return MongoClientOptions.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java index 41bbc9231a..232096fd3e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java @@ -30,8 +30,6 @@ import org.springframework.util.StringUtils; import com.mongodb.Mongo; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; import com.mongodb.MongoCredential; import com.mongodb.MongoOptions; import com.mongodb.ServerAddress; @@ -46,14 +44,15 @@ * @author Thomas Darimont * @author Christoph Strobl * @since 1.0 + * @deprecated since 1.7. Please use {@link MongoClientFactoryBean} instead. */ +@Deprecated public class MongoFactoryBean implements FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator { private Mongo mongo; private MongoOptions mongoOptions; - private MongoClientOptions mongoClientOptions; private String host; private Integer port; @@ -66,33 +65,11 @@ public class MongoFactoryBean implements FactoryBean, InitializingBean, D /** * @param mongoOptions - * @deprecated since 1.7. Please use {@link #setMongoClientOptions(MongoClientOptions)} */ - @Deprecated public void setMongoOptions(MongoOptions mongoOptions) { this.mongoOptions = mongoOptions; } - /** - * Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}. - * - * @param mongoClientOptions - * @since 1.7 - */ - public void setMongoClientOptions(MongoClientOptions mongoClientOptions) { - this.mongoClientOptions = mongoClientOptions; - } - - /** - * Set the list of credentials to be used when creating {@link MongoClient}. - * - * @param credentials can be {@literal null}. - * @since 1.7 - */ - public void setCredentials(List credentials) { - this.credentials = credentials; - } - public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) { this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds); } @@ -181,19 +158,9 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { - this.mongo = mongoClientOptions != null ? createMongoClient() : createMongo(); - } - - private MongoClient createMongoClient() { - - if (!isNullOrEmpty(replicaPair)) { - throw new CannotGetMongoDbConnectionException( - "Cannot create MongoClient with replicaPair please use replicaSetSeeds."); - } - return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions); + this.mongo = createMongo(); } - @SuppressWarnings("deprecation") private Mongo createMongo() throws UnknownHostException { Mongo mongo; diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas index 10b176c4df..5c6d289693 100644 --- a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas +++ b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas @@ -4,4 +4,5 @@ http\://www.springframework.org/schema/data/mongo/spring-mongo-1.2.xsd=org/sprin http\://www.springframework.org/schema/data/mongo/spring-mongo-1.3.xsd=org/springframework/data/mongodb/config/spring-mongo-1.3.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.4.xsd=org/springframework/data/mongodb/config/spring-mongo-1.4.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-1.5.xsd=org/springframework/data/mongodb/config/spring-mongo-1.5.xsd -http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.5.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo-1.7.xsd=org/springframework/data/mongodb/config/spring-mongo-1.7.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.7.xsd diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd new file mode 100644 index 0000000000..7347aada61 --- /dev/null +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd @@ -0,0 +1,868 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + The reference to a MongoTemplate. Will default to 'mongoTemplate'. + + + + + + + Enables creation of indexes for queries that get derived from the method name + and thus reference domain class properties. Defaults to false. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + The reference to a MongoTypeMapper to be used by this MappingMongoConverter. + + + + + + + The reference to a MappingContext. Will default to 'mappingContext'. + + + + + + + Disables JSR-303 validation on MongoDB documents before they are saved. By default it is set to false. + + + + + + + + + + Enables abbreviating the field names for domain class properties to the + first character of their camel case names, e.g. fooBar -> fb. Defaults to false. + + + + + + + + + + The reference to a FieldNamingStrategy. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A reference to a custom converter. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a DbFactory. + + + + + + + + + + + + + + + + diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java new file mode 100644 index 0000000000..223c678a73 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import static org.hamcrest.core.Is.*; +import static org.hamcrest.core.IsInstanceOf.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.support.BeanDefinitionReader; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.mongodb.MongoClient; +import com.mongodb.ReadPreference; +import com.mongodb.WriteConcern; + +/** + * Integration tests for {@link MongoClientParser}. + * + * @author Christoph Strobl + */ +public class MongoClientParserIntegrationTests { + + DefaultListableBeanFactory factory; + BeanDefinitionReader reader; + + @Before + public void setUp() { + + this.factory = new DefaultListableBeanFactory(); + this.reader = new XmlBeanDefinitionReader(factory); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void createsMongoClientCorrectlyWhenGivenHostAndPort() { + + reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); + + assertThat(factory.getBean("mongo-client-with-host-and-port"), instanceOf(MongoClient.class)); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void createsMongoClientWithOptionsCorrectly() { + + reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); + + AbstractApplicationContext context = new GenericApplicationContext(factory); + context.refresh(); + + try { + MongoClient client = context.getBean("mongo-client-with-options-for-write-concern-and-read-preference", + MongoClient.class); + + assertThat(client.getReadPreference(), is(ReadPreference.secondary())); + assertThat(client.getWriteConcern(), is(WriteConcern.NORMAL)); + } finally { + context.close(); + } + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void createsMongoClientWithDefaultsCorrectly() { + + reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); + + AbstractApplicationContext context = new GenericApplicationContext(factory); + context.refresh(); + + try { + MongoClient client = context.getBean("mongo", MongoClient.class); + + assertThat(client.getAddress().getHost(), is("127.0.0.1")); + assertThat(client.getAddress().getPort(), is(27017)); + } finally { + context.close(); + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java new file mode 100644 index 0000000000..792d3d4143 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import org.hamcrest.core.Is; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.mongodb.ReadPreference; + +/** + * @author Christoph Strobl + */ +public class ReadPreferencePropertyEditorUnitTests { + + @Rule public ExpectedException expectedException = ExpectedException.none(); + + ReadPreferencePropertyEditor editor; + + @Before + public void setUp() { + editor = new ReadPreferencePropertyEditor(); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldThrowExceptionOnUndefinedPreferenceString() { + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("ReadPreference"); + expectedException.expectMessage("foo"); + + editor.setAsText("foo"); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldAllowUsageNativePreferenceStrings() { + + editor.setAsText("secondary"); + + Assert.assertThat(editor.getValue(), Is. is(ReadPreference.secondary())); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldAllowUsageOfUppcaseEnumStringsForPreferences() { + + editor.setAsText("NEAREST"); + + Assert.assertThat(editor.getValue(), Is. is(ReadPreference.nearest())); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBeanIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBeanIntegrationTests.java new file mode 100644 index 0000000000..6d73e4e113 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientOptionsFactoryBeanIntegrationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.core; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.data.mongodb.config.ReadPreferencePropertyEditor; +import org.springframework.test.util.ReflectionTestUtils; + +import com.mongodb.ReadPreference; + +/** + * @author Christoph Strobl + */ +public class MongoClientOptionsFactoryBeanIntegrationTests { + + /** + * @see DATAMONGO-1158 + */ + @Test + public void convertsReadPreferenceConcernCorrectly() { + + RootBeanDefinition definition = new RootBeanDefinition(MongoClientOptionsFactoryBean.class); + definition.getPropertyValues().addPropertyValue("readPreference", "NEAREST"); + + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + factory.registerCustomEditor(ReadPreference.class, ReadPreferencePropertyEditor.class); + + factory.registerBeanDefinition("factory", definition); + + MongoClientOptionsFactoryBean bean = factory.getBean("&factory", MongoClientOptionsFactoryBean.class); + assertThat(ReflectionTestUtils.getField(bean, "readPreference"), is((Object) ReadPreference.nearest())); + } + +} diff --git a/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml b/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml new file mode 100644 index 0000000000..f923b799c1 --- /dev/null +++ b/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/main/asciidoc/reference/mongo-3.adoc b/src/main/asciidoc/reference/mongo-3.adoc index 0f7f6f57e1..716d153d7a 100644 --- a/src/main/asciidoc/reference/mongo-3.adoc +++ b/src/main/asciidoc/reference/mongo-3.adoc @@ -18,6 +18,25 @@ Some of the configuration options have been changed / removed for the _mongo-jav * maxAutoConnectRetryTime * slaveOk +Generally it is recommended to use the `` and `` elements instead of `` when doing XML based configuration, since those elements will only provide you with attributes valid for the 3 generation java driver. + +[source,xml] +---- + + + + + + + + +---- + + === WriteConcern and WriteConcernChecking The `WriteConcern.NONE`, which had been used as default by Spring Data MongoDB, was removed in 3.0. Therefore in a MongoDB 3 environment the `WriteConcern` will be defaulted to `WriteConcern.UNACKNOWLEGED`. In case `WriteResultChecking.EXCEPTION` is enabled the `WriteConcern` will be altered to `WriteConcern.ACKNOWLEDGED` for write operations, as otherwise errors during execution would not be throw correctly, since simply not raised by the driver. From acab48daa8552931b8a943a1f1e3ca59e9894dbb Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 26 Feb 2015 20:31:44 +0100 Subject: [PATCH 7/8] DATAMONGO-1158 - Add config option for credentials to mongo-client. Added credentials attribute to which allows to define a set of credentials used for setting up the MongoClient correctly using authentication data. --- .../mongodb/config/MongoClientParser.java | 5 + .../config/MongoCredentialPropertyEditor.java | 124 +++++++++++++ .../mongodb/config/MongoParsingUtils.java | 32 +++- .../mongodb/core/MongoClientFactoryBean.java | 4 +- .../data/mongodb/core/MongoFactoryBean.java | 2 - .../data/mongodb/config/spring-mongo-1.7.xsd | 2 +- .../MongoClientParserIntegrationTests.java | 23 +++ ...ongoCredentialPropertyEditorUnitTests.java | 169 ++++++++++++++++++ .../resources/namespace/mongoClient-bean.xml | 2 + src/main/asciidoc/reference/mongo-3.adoc | 17 +- 10 files changed, 367 insertions(+), 13 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java index c95c31ba1f..94159c1836 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoClientParser.java @@ -50,6 +50,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { ParsingUtils.setPropertyValue(builder, element, "port", "port"); ParsingUtils.setPropertyValue(builder, element, "host", "host"); + ParsingUtils.setPropertyValue(builder, element, "credentials", "credentials"); MongoParsingUtils.parseMongoClientOptions(element, builder); MongoParsingUtils.parseReplicaSet(element, builder); @@ -73,6 +74,10 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { .getReadPreferencePropertyEditorBuilder()); parserContext.registerBeanComponent(readPreferenceEditor); + BeanComponentDefinition credentialsEditor = helper.getComponent(MongoParsingUtils + .getMongoCredentialPropertyEditor()); + parserContext.registerBeanComponent(credentialsEditor); + parserContext.popAndRegisterContainingComponent(); return mongoComponent.getBeanDefinition(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java new file mode 100644 index 0000000000..f90a5f5766 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java @@ -0,0 +1,124 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import java.beans.PropertyEditorSupport; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.springframework.util.StringUtils; + +import com.mongodb.MongoCredential; + +/** + * Parse a {@link String} to a Collection of {@link MongoCredential}. + * + * @author Christoph Strobl + * @since 1.7 + */ +public class MongoCredentialPropertyEditor extends PropertyEditorSupport { + + private static final String AUTH_MECHANISM_KEY = "uri.authMechanism"; + private static final String USERNAME_PASSWORD_DELIMINATOR = ":"; + private static final String DATABASE_DELIMINATOR = "@"; + private static final String OPTIONS_DELIMINATOR = "?"; + private static final String OPTION_VALUE_DELIMINATOR = "&"; + + @Override + public void setAsText(String text) throws IllegalArgumentException { + + if (!StringUtils.hasText(text)) { + return; + } + + List credentials = new ArrayList(); + for (String credentialString : text.split(",")) { + + if (!text.contains(USERNAME_PASSWORD_DELIMINATOR) || !text.contains(DATABASE_DELIMINATOR)) { + throw new IllegalArgumentException("Credentials need to be in format 'username:password@database'!"); + } + + String[] userNameAndPassword = extractUserNameAndPassword(credentialString); + String database = extractDB(credentialString); + Properties options = extractOptions(credentialString); + + if (!options.isEmpty()) { + if (options.containsKey(AUTH_MECHANISM_KEY)) { + String authMechanism = options.getProperty(AUTH_MECHANISM_KEY); + if (MongoCredential.GSSAPI_MECHANISM.equals(authMechanism)) { + credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0])); + } else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) { + credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database, + userNameAndPassword[1].toCharArray())); + } else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) { + credentials.add(MongoCredential.createMongoX509Credential(userNameAndPassword[0])); + } else if (MongoCredential.PLAIN_MECHANISM.equals(authMechanism)) { + credentials.add(MongoCredential.createPlainCredential(userNameAndPassword[0], database, + userNameAndPassword[1].toCharArray())); + } else if (MongoCredential.SCRAM_SHA_1_MECHANISM.equals(authMechanism)) { + credentials.add(MongoCredential.createScramSha1Credential(userNameAndPassword[0], database, + userNameAndPassword[1].toCharArray())); + } else { + throw new IllegalArgumentException(String.format( + "Cannot create MongoCredentials for unknown auth mechanism '%s'!", authMechanism)); + } + } + } else { + credentials.add(MongoCredential.createCredential(userNameAndPassword[0], database, + userNameAndPassword[1].toCharArray())); + } + } + + setValue(credentials); + } + + private String[] extractUserNameAndPassword(String text) { + + int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR); + String userNameAndPassword = text.substring(0, dbSeperationIndex); + return userNameAndPassword.split(USERNAME_PASSWORD_DELIMINATOR); + } + + private String extractDB(String text) { + + int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR); + + String tmp = text.substring(dbSeperationIndex + 1); + int optionsSeperationIndex = tmp.lastIndexOf(OPTIONS_DELIMINATOR); + + return optionsSeperationIndex > -1 ? tmp.substring(0, optionsSeperationIndex) : tmp; + } + + private Properties extractOptions(String text) { + + int optionsSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR); + int dbSeperationIndex = text.lastIndexOf(OPTIONS_DELIMINATOR); + + if (optionsSeperationIndex == -1 || dbSeperationIndex > optionsSeperationIndex) { + return new Properties(); + } + + Properties properties = new Properties(); + + for (String option : text.substring(optionsSeperationIndex + 1).split(OPTION_VALUE_DELIMINATOR)) { + String[] optionArgs = option.split("="); + properties.put(optionArgs[0], optionArgs[1]); + } + + return properties; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index 9f71aa4ce9..7d78b22a79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -147,6 +147,22 @@ static BeanDefinitionBuilder getWriteConcernPropertyEditorBuilder() { return builder; } + /** + * One should only register one bean definition but want to have the convenience of using + * AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the + * container. + */ + static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() { + + Map customEditors = new ManagedMap(); + customEditors.put("com.mongodb.ServerAddress[]", + "org.springframework.data.mongodb.config.ServerAddressPropertyEditor"); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); + builder.addPropertyValue("customEditors", customEditors); + return builder; + } + /** * Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a * {@link ReadPreferencePropertyEditor}. @@ -166,18 +182,20 @@ static BeanDefinitionBuilder getReadPreferencePropertyEditorBuilder() { } /** - * One should only register one bean definition but want to have the convenience of using - * AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the - * container. + * Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a + * {@link MongoCredentialPropertyEditor}. + * + * @return + * @since 1.7 */ - static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() { + static BeanDefinitionBuilder getMongoCredentialPropertyEditor() { - Map customEditors = new ManagedMap(); - customEditors.put("com.mongodb.ServerAddress[]", - "org.springframework.data.mongodb.config.ServerAddressPropertyEditor"); + Map> customEditors = new ManagedMap>(); + customEditors.put("com.mongodb.MongoCredential[]", MongoCredentialPropertyEditor.class); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); builder.addPropertyValue("customEditors", customEditors); + return builder; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java index 4794b90a40..c1c4edd61f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java @@ -68,8 +68,8 @@ public void setMongoClientOptions(MongoClientOptions mongoClientOptions) { * * @param credentials can be {@literal null}. */ - public void setCredentials(List credentials) { - this.credentials = credentials; + public void setCredentials(MongoCredential[] credentials) { + this.credentials = filterNonNullElementsAsList(credentials); } public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java index 232096fd3e..41699be8e6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoFactoryBean.java @@ -30,7 +30,6 @@ import org.springframework.util.StringUtils; import com.mongodb.Mongo; -import com.mongodb.MongoCredential; import com.mongodb.MongoOptions; import com.mongodb.ServerAddress; import com.mongodb.WriteConcern; @@ -59,7 +58,6 @@ public class MongoFactoryBean implements FactoryBean, InitializingBean, D private WriteConcern writeConcern; private List replicaSetSeeds; private List replicaPair; - private List credentials; private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd index 7347aada61..d70e26be55 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.7.xsd @@ -586,7 +586,7 @@ The comma delimited list of host:port entries to use for replica set/pairs. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java index 223c678a73..a2abd7c6e1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.config; +import static org.hamcrest.collection.IsIterableContainingInOrder.*; import static org.hamcrest.core.Is.*; import static org.hamcrest.core.IsInstanceOf.*; import static org.junit.Assert.*; @@ -29,6 +30,7 @@ import org.springframework.core.io.ClassPathResource; import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; @@ -102,4 +104,25 @@ public void createsMongoClientWithDefaultsCorrectly() { context.close(); } } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void createsMongoClientWithCredentialsCorrectly() { + + reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); + + AbstractApplicationContext context = new GenericApplicationContext(factory); + context.refresh(); + + try { + MongoClient client = context.getBean("mongo-client-with-credentials", MongoClient.class); + + assertThat(client.getCredentialsList(), + contains(MongoCredential.createPlainCredential("jon", "snow", "warg".toCharArray()))); + } finally { + context.close(); + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java new file mode 100644 index 0000000000..1fc321597d --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java @@ -0,0 +1,169 @@ +/* + * Copyright 2015 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 + * + * 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 org.springframework.data.mongodb.config; + +import static org.hamcrest.collection.IsIterableContainingInOrder.*; +import static org.hamcrest.core.IsNull.*; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.util.StringUtils; + +import com.mongodb.MongoCredential; + +/** + * @author Christoph Strobl + */ +public class MongoCredentialPropertyEditorUnitTests { + + static final String USER_1_NAME = "tyrion"; + static final String USER_1_PWD = "dwarf"; + static final String USER_1_DB = "lannister"; + + static final String USER_2_NAME = "jon"; + static final String USER_2_PWD = "warg"; + static final String USER_2_DB = "snow"; + + static final String USER_1_AUTH_STRING = USER_1_NAME + ":" + USER_1_PWD + "@" + USER_1_DB; + static final String USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM = USER_1_AUTH_STRING + "?uri.authMechanism=PLAIN"; + + static final String USER_2_AUTH_STRING = USER_2_NAME + ":" + USER_2_PWD + "@" + USER_2_DB; + static final String USER_2_AUTH_STRING_WITH_MONGODB_CR_AUTH_MECHANISM = USER_2_AUTH_STRING + + "?uri.authMechanism=MONGODB-CR"; + + static final MongoCredential USER_1_CREDENTIALS = MongoCredential.createCredential(USER_1_NAME, USER_1_DB, + USER_1_PWD.toCharArray()); + static final MongoCredential USER_1_CREDENTIALS_PLAIN_AUTH = MongoCredential.createPlainCredential(USER_1_NAME, + USER_1_DB, USER_1_PWD.toCharArray()); + + static final MongoCredential USER_2_CREDENTIALS = MongoCredential.createCredential(USER_2_NAME, USER_2_DB, + USER_2_PWD.toCharArray()); + static final MongoCredential USER_2_CREDENTIALS_CR_AUTH = MongoCredential.createMongoCRCredential(USER_2_NAME, + USER_2_DB, USER_2_PWD.toCharArray()); + + MongoCredentialPropertyEditor editor; + + @Before + public void setUp() { + this.editor = new MongoCredentialPropertyEditor(); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldReturnNullValueForNullText() { + + editor.setAsText(null); + + assertThat(editor.getValue(), nullValue()); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + public void shouldReturnNullValueForEmptyText() { + + editor.setAsText(" "); + + assertThat(editor.getValue(), nullValue()); + } + + /** + * @see DATAMONGO-1158 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForMalformatedCredentialsString() { + editor.setAsText("tyrion"); + } + + /** + * @see DATAMONGO-1158 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForMalformatedAuthMechanism() { + editor.setAsText(USER_2_AUTH_STRING + "?uri.authMechanism=Targaryen"); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleUserNamePasswordStringWithDatabaseAndNoOptions() { + + editor.setAsText(USER_1_AUTH_STRING); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS)); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleUserNamePasswordStringWithDatabaseAndAuthOptions() { + + editor.setAsText(USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS_PLAIN_AUTH)); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleUserNamePasswordStringWithDatabaseAndNoOptions() { + + editor + .setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList(USER_1_AUTH_STRING, USER_2_AUTH_STRING))); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS, USER_2_CREDENTIALS)); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleUserNamePasswordStringWithDatabaseAndAuthOptions() { + + editor.setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList( + USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM, USER_2_AUTH_STRING_WITH_MONGODB_CR_AUTH_MECHANISM))); + + assertThat((List) editor.getValue(), + contains(USER_1_CREDENTIALS_PLAIN_AUTH, USER_2_CREDENTIALS_CR_AUTH)); + } + + /** + * @see DATAMONGO-1158 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleUserNamePasswordStringWithDatabaseAndMixedOptions() { + + editor.setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList( + USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM, USER_2_AUTH_STRING))); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS_PLAIN_AUTH, USER_2_CREDENTIALS)); + } +} diff --git a/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml b/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml index f923b799c1..8f6a36b231 100644 --- a/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml +++ b/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml @@ -11,6 +11,8 @@ + + diff --git a/src/main/asciidoc/reference/mongo-3.adoc b/src/main/asciidoc/reference/mongo-3.adoc index 716d153d7a..6b42b9a7d0 100644 --- a/src/main/asciidoc/reference/mongo-3.adoc +++ b/src/main/asciidoc/reference/mongo-3.adoc @@ -36,7 +36,6 @@ Generally it is recommended to use the `` and ` ---- - === WriteConcern and WriteConcernChecking The `WriteConcern.NONE`, which had been used as default by Spring Data MongoDB, was removed in 3.0. Therefore in a MongoDB 3 environment the `WriteConcern` will be defaulted to `WriteConcern.UNACKNOWLEGED`. In case `WriteResultChecking.EXCEPTION` is enabled the `WriteConcern` will be altered to `WriteConcern.ACKNOWLEDGED` for write operations, as otherwise errors during execution would not be throw correctly, since simply not raised by the driver. @@ -64,6 +63,22 @@ public class ApplicationContextEventTestsAppConfig extends AbstractMongoConfigur } ---- +In order to use authentication with XML configuration use the `credentials` attribue on ``. + +[source,xml] +---- + + + + + + +---- + === Other things to be aware of This section covers additional things to keep in mind when using the 3.0 driver. From 7bfbc447a4c2e4f3e166756406379724cfed9afd Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 2 Mar 2015 16:28:25 +0100 Subject: [PATCH 8/8] DATAMONGO-1158 - Add config option for credentials to mongo-client. We now reject mongo-options configuration when using mongo-java-driver generation 3 and above. --- .../mongodb/core/MongoOptionsFactoryBean.java | 19 ++++++++++++------- .../MongoDbFactoryParserIntegrationTests.java | 10 +++++++--- .../mongodb/config/MongoNamespaceTests.java | 17 ++++++++++------- .../MongoOptionsFactoryBeanUnitTests.java | 8 ++++++-- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java index 0fc2991666..72a05aa97d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBean.java @@ -32,8 +32,9 @@ * @author Mike Saavedra * @author Thomas Darimont * @author Christoph Strobl + * @deprecated since 1.7. Please use {@link MongoClientOptionsFactoryBean} instead. */ -@SuppressWarnings("deprecation") +@Deprecated public class MongoOptionsFactoryBean implements FactoryBean, InitializingBean { private static final MongoOptions DEFAULT_MONGO_OPTIONS = new MongoOptions(); @@ -216,6 +217,12 @@ public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { */ public void afterPropertiesSet() { + if (MongoClientVersion.isMongo3Driver()) { + throw new IllegalArgumentException( + String + .format("Usage of 'mongo-options' is no longer supported for mongo-java-driver version 3 and above. Please use 'mongo-client-options' and refer to chapter 'MongoDB 3.0 Support' for details.")); + } + MongoOptions options = new MongoOptions(); options.setConnectionsPerHost(connectionsPerHost); @@ -233,12 +240,9 @@ public void afterPropertiesSet() { options.setSocketFactory(sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault()); } - if (!MongoClientVersion.isMongo3Driver()) { - - ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry); - ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime); - ReflectiveMongoOptionsInvoker.setSlaveOk(options, slaveOk); - } + ReflectiveMongoOptionsInvoker.setAutoConnectRetry(options, autoConnectRetry); + ReflectiveMongoOptionsInvoker.setMaxAutoConnectRetryTime(options, maxAutoConnectRetryTime); + ReflectiveMongoOptionsInvoker.setSlaveOk(options, slaveOk); this.options = options; } @@ -266,4 +270,5 @@ public Class getObjectType() { public boolean isSingleton() { return true; } + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java index 4e94919bf3..fed8c6083d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java @@ -21,6 +21,7 @@ import static org.springframework.data.mongodb.MongoClientVersion.*; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; @@ -47,13 +48,18 @@ * Integration tests for {@link MongoDbFactoryParser}. * * @author Oliver Gierke - * @auhtor Christoph Strobl + * @author Christoph Strobl */ public class MongoDbFactoryParserIntegrationTests { DefaultListableBeanFactory factory; BeanDefinitionReader reader; + @BeforeClass + public static void validateMongoDriver() { + assumeFalse(isMongo3Driver()); + } + @Before public void setUp() { factory = new DefaultListableBeanFactory(); @@ -136,8 +142,6 @@ public void createsDbFactoryBean() { @Test public void parsesMaxAutoConnectRetryTimeCorrectly() { - assumeFalse(isMongo3Driver()); - reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml")); Mongo mongo = factory.getBean(Mongo.class); assertThat(ReflectiveMongoOptionsInvokerTestUtil.getMaxAutoConnectRetryTime(mongo.getMongoOptions()), is(27L)); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java index 0f79727fe6..7d90c0532e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java @@ -16,11 +16,13 @@ package org.springframework.data.mongodb.config; import static org.junit.Assert.*; +import static org.junit.Assume.*; import static org.springframework.data.mongodb.MongoClientVersion.*; import static org.springframework.test.util.ReflectionTestUtils.*; import javax.net.ssl.SSLSocketFactory; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +56,11 @@ public class MongoNamespaceTests { @Autowired ApplicationContext ctx; + @BeforeClass + public static void validateMongoDriver() { + assumeFalse(isMongo3Driver()); + } + @Test public void testMongoSingleton() throws Exception { @@ -251,12 +258,8 @@ public void testMongoSingletonWithPropertyPlaceHolders() throws Exception { assertEquals(0, mongoOpts.getWriteConcern().getWtimeout()); assertEquals(true, mongoOpts.getWriteConcern().fsync()); - if (!isMongo3Driver()) { - assertEquals(true, mongoOpts.fsync); - assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getAutoConnectRetry(mongoOpts)); - assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getSlaveOk(mongoOpts)); - } else { - assertEquals(false, mongoOpts.fsync); - } + assertEquals(true, mongoOpts.fsync); + assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getAutoConnectRetry(mongoOpts)); + assertEquals(true, ReflectiveMongoOptionsInvokerTestUtil.getSlaveOk(mongoOpts)); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java index 61643bba60..a2a935ef49 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOptionsFactoryBeanUnitTests.java @@ -23,6 +23,7 @@ import javax.net.ssl.SSLSocketFactory; +import org.junit.BeforeClass; import org.junit.Test; import com.mongodb.MongoOptions; @@ -36,14 +37,17 @@ */ public class MongoOptionsFactoryBeanUnitTests { + @BeforeClass + public static void validateMongoDriver() { + assumeFalse(isMongo3Driver()); + } + /** * @see DATADOC-280 */ @Test public void setsMaxConnectRetryTime() { - assumeFalse(isMongo3Driver()); - MongoOptionsFactoryBean bean = new MongoOptionsFactoryBean(); bean.setMaxAutoConnectRetryTime(27); bean.afterPropertiesSet();