From 325e356bb8b80bc845df282a96c8d2f7f301a403 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Wed, 26 Jan 2011 13:06:34 -0500 Subject: [PATCH] DATADOC-14 added WriteConcern for MongoTemplate to be used for write operations when specified and also methods to set WriteConcern for DB and Collections --- .../document/mongodb/MongoOperations.java | 26 ++++ .../data/document/mongodb/MongoTemplate.java | 127 ++++++++++++++++-- .../MongoTemplateWriteConcernTests.java | 86 ++++++++++++ 3 files changed, 228 insertions(+), 11 deletions(-) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateWriteConcernTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java index e0cf1b6a6..78fc81a31 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoOperations.java @@ -20,6 +20,7 @@ import java.util.Set; import com.mongodb.DBCollection; import com.mongodb.DBObject; +import com.mongodb.WriteConcern; /** * Interface that specifies a basic set of MongoDB operations. Implemented by {@link MongoTemplate}. @@ -44,6 +45,31 @@ public interface MongoOperations { */ DBCollection getDefaultCollection(); + /** + * Set the {@link com.mongodb.WriteConcern} to be used for the Database. + * @param writeConcern + */ + void setDatabaseWriteConcern(WriteConcern writeConcern); + + /** + * Get the {@link com.mongodb.WriteConcern} currently used by the Database. + * @return the WriteConcern + */ + WriteConcern getDatabaseWriteConcern(); + + /** + * Set the {@link com.mongodb.WriteConcern} to be used for the Collection. + * @param collectionName + * @param writeConcern + */ + void setCollectionWriteConcern(String collectionName, WriteConcern writeConcern); + + /** + * Get the {@link com.mongodb.WriteConcern} currently used by the Collection. + * @param collectionName + * @return the WriteConcern + */ + WriteConcern getCollectionWriteConcern(String collectionName); /** * Execute the a MongoDB command expressed as a JSON string. This will call the method diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java index d41d926ac..e0c2c1b82 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java @@ -39,6 +39,7 @@ import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.Mongo; import com.mongodb.MongoException; +import com.mongodb.WriteConcern; import com.mongodb.util.JSON; /** @@ -53,6 +54,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { private static final String ID = "_id"; + /* + * WriteConcern to be used for write operations if it has been specified. Otherwise + * we should not use a WriteConcern defaulting to the one set for the DB or Collection. + */ + private WriteConcern writeConcern = null; + private final MongoConverter mongoConverter; private final Mongo mongo; private final MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); @@ -62,20 +69,70 @@ public class MongoTemplate implements InitializingBean, MongoOperations { private String username; private String password; - + + /** + * Constructor used for a basic template configuration + * @param mongo + * @param databaseName + */ public MongoTemplate(Mongo mongo, String databaseName) { - this(mongo, databaseName, null, null); + this(mongo, databaseName, null, null, null); } + /** + * Constructor used for a basic template configuration with a specific {@link com.mongodb.WriteConcern} + * to be used for all database write operations + * @param mongo + * @param databaseName + * @param writeConcern + */ + public MongoTemplate(Mongo mongo, String databaseName, WriteConcern writeConcern) { + this(mongo, databaseName, null, null, writeConcern); + } + + /** + * Constructor used for a basic template configuration with a default collection name + * @param mongo + * @param databaseName + * @param defaultCollectionName + */ public MongoTemplate(Mongo mongo, String databaseName, String defaultCollectionName) { - this(mongo, databaseName, defaultCollectionName, null); + this(mongo, databaseName, defaultCollectionName, null, null); } - public MongoTemplate(Mongo mongo, String databaseName, MongoConverter mongoConverter) { - this(mongo, databaseName, null, mongoConverter); + /** + * Constructor used for a basic template configuration with a default collection name and + * with a specific {@link com.mongodb.WriteConcern} to be used for all database write operations + * @param mongo + * @param databaseName + * @param defaultCollectionName + * @param writeConcern + */ + public MongoTemplate(Mongo mongo, String databaseName, String defaultCollectionName, WriteConcern writeConcern) { + this(mongo, databaseName, defaultCollectionName, null, writeConcern); } + /** + * Constructor used for a template configuration with a default collection name and a custom {@link MongoConverter} + * @param mongo + * @param databaseName + * @param defaultCollectionName + * @param mongoConverter + */ public MongoTemplate(Mongo mongo, String databaseName, String defaultCollectionName, MongoConverter mongoConverter) { + this(mongo, databaseName, defaultCollectionName, mongoConverter, null); + } + + /** + * Constructor used for a template configuration with a default collection name and a custom {@link MongoConverter} + * and with a specific {@link com.mongodb.WriteConcern} to be used for all database write operations + * @param mongo + * @param databaseName + * @param defaultCollectionName + * @param mongoConverter + * @param writeConcern + */ + public MongoTemplate(Mongo mongo, String databaseName, String defaultCollectionName, MongoConverter mongoConverter, WriteConcern writeConcern) { Assert.notNull(mongo); Assert.notNull(databaseName); @@ -84,6 +141,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations { this.defaultCollectionName = defaultCollectionName; this.mongo = mongo; this.databaseName = databaseName; + this.writeConcern = writeConcern; } @@ -153,6 +211,23 @@ public class MongoTemplate implements InitializingBean, MongoOperations { }); } + // TODO: + public void setDatabaseWriteConcern(WriteConcern writeConcern) { + getDb().setWriteConcern(writeConcern); + } + + public WriteConcern getDatabaseWriteConcern() { + return getDb().getWriteConcern(); + } + + public void setCollectionWriteConcern(String collectionName, WriteConcern writeConcern) { + getCollection(collectionName).setWriteConcern(writeConcern); + } + + public WriteConcern getCollectionWriteConcern(String collectionName) { + return getCollection(collectionName).getWriteConcern(); + } + /* (non-Javadoc) * @see org.springframework.data.document.mongodb.MongoOperations#executeCommand(java.lang.String) */ @@ -430,7 +505,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { return execute(new CollectionCallback() { public ObjectId doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.insert(dbDoc); + if (writeConcern == null) { + collection.insert(dbDoc); + } + else { + collection.insert(dbDoc, writeConcern); + } return (ObjectId) dbDoc.get(ID); } }, collectionName); @@ -447,7 +527,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { execute(new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.insert(dbDocList); + if (writeConcern == null) { + collection.insert(dbDocList); + } + else { + collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcern); + } return null; } }, collectionName); @@ -474,7 +559,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { return execute(new CollectionCallback() { public ObjectId doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.save(dbDoc); + if (writeConcern == null) { + collection.save(dbDoc); + } + else { + collection.save(dbDoc, writeConcern); + } return (ObjectId) dbDoc.get(ID); } }, collectionName); @@ -493,7 +583,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { public void updateFirst(String collectionName, final DBObject queryDoc, final DBObject updateDoc) { execute(new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.update(queryDoc, updateDoc); + if (writeConcern == null) { + collection.update(queryDoc, updateDoc); + } + else { + collection.update(queryDoc, updateDoc, false, false, writeConcern); + } return null; } }, collectionName); @@ -512,7 +607,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { public void updateMulti(String collectionName, final DBObject queryDoc, final DBObject updateDoc) { execute(new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.updateMulti(queryDoc, updateDoc); + if (writeConcern == null) { + collection.updateMulti(queryDoc, updateDoc); + } + else { + collection.update(queryDoc, updateDoc, false, true, writeConcern); + } return null; } }, collectionName); @@ -531,7 +631,12 @@ public class MongoTemplate implements InitializingBean, MongoOperations { public void remove(String collectionName, final DBObject queryDoc) { execute(new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { - collection.remove(queryDoc); + if (writeConcern == null) { + collection.remove(queryDoc); + } + else { + collection.remove(queryDoc, writeConcern); + } return null; } }, collectionName); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateWriteConcernTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateWriteConcernTests.java new file mode 100644 index 000000000..540666df0 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoTemplateWriteConcernTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2011 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.document.mongodb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mongodb.WriteConcern; + +/** + * Integration test of the WriteConcern features for {@link MongoTemplate}. + * + * @author Thomas Risberg + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:infrastructure.xml") +public class MongoTemplateWriteConcernTests { + + @Autowired + MongoTemplate template; + + @Before + public void setUp() { + template.dropCollection(template.getDefaultCollectionName()); + } + + @After + public void tearDown() { + template.setCollectionWriteConcern(template.getDefaultCollectionName(), WriteConcern.NORMAL); + template.setDatabaseWriteConcern(WriteConcern.NORMAL); + } + + @Test + public void testRetrievingTheDatabaseWriteConcern() throws Exception { + WriteConcern wc = template.getDatabaseWriteConcern(); + assertNotNull(wc); + } + + @Test + public void testRetrievingTheCollectionWriteConcern() throws Exception { + WriteConcern wc = template.getCollectionWriteConcern(template.getDefaultCollectionName()); + assertNotNull(wc); + } + + @Test + public void testSettingTheDatabaseWriteConcern() throws Exception { + WriteConcern wc = template.getDatabaseWriteConcern(); + WriteConcern safe = WriteConcern.SAFE; + template.setDatabaseWriteConcern(safe); + assertEquals(safe.getW(), template.getDatabaseWriteConcern().getW()); + assertNotSame(wc.getW(), template.getDatabaseWriteConcern().getW()); + } + + @Test + public void testSettingTheCollectionWriteConcern() throws Exception { + String coll = template.getDefaultCollectionName(); + WriteConcern replicasSafe = WriteConcern.REPLICAS_SAFE; + WriteConcern fsyncSafe = WriteConcern.FSYNC_SAFE; + template.setDatabaseWriteConcern(fsyncSafe); + template.setCollectionWriteConcern(coll, replicasSafe); + assertEquals(replicasSafe.getW(), template.getCollectionWriteConcern(coll).getW()); + assertEquals(replicasSafe.fsync(), template.getCollectionWriteConcern(coll).fsync()); + } +}