From 597b4374bc91f84ce3f49acf8b64872758ae8d49 Mon Sep 17 00:00:00 2001
From: Oliver Gierke
Date: Wed, 2 Mar 2011 11:16:22 +0100
Subject: [PATCH 1/9] DATADOC-49 - Fixed And and Or keyword handling.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Made Query.or(…) public and changed MongoQueryCreator to operate on Query rather than on Criteria.
---
.../data/document/mongodb/query/Query.java | 2 +-
.../mongodb/repository/MongoQueryCreator.java | 25 +++++++++----------
...tractPersonRepositoryIntegrationTests.java | 16 ++++++++++++
.../document/mongodb/repository/Person.java | 8 ++++++
.../mongodb/repository/PersonRepository.java | 4 +++
5 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Query.java
index 3bf36ec7a..6e8756e71 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Query.java
@@ -44,7 +44,7 @@ public class Query {
return this;
}
- protected Query or(Query... queries) {
+ public Query or(Query... queries) {
this.criteria.put("$or", new OrCriteria(queries));
return this;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java
index f5478b68d..eef11343f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java
@@ -18,7 +18,6 @@ package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.query.Criteria.*;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.regex.Pattern;
@@ -41,7 +40,7 @@ import org.springframework.data.repository.query.parser.PartTree;
*
* @author Oliver Gierke
*/
-class MongoQueryCreator extends AbstractQueryCreator {
+class MongoQueryCreator extends AbstractQueryCreator {
private static final Logger LOG = LoggerFactory.getLogger(MongoQueryCreator.class);
@@ -64,10 +63,12 @@ class MongoQueryCreator extends AbstractQueryCreator {
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
*/
@Override
- protected Criteria create(Part part, Iterator iterator) {
+ protected Query create(Part part, Iterator iterator) {
- return from(part.getType(),
+ Criteria criteria = from(part.getType(),
where(part.getProperty().toDotPath()), iterator);
+
+ return new Query(criteria);
}
@@ -76,11 +77,12 @@ class MongoQueryCreator extends AbstractQueryCreator {
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
*/
@Override
- protected Criteria and(Part part, Criteria base,
+ protected Query and(Part part, Query base,
Iterator iterator) {
- return from(part.getType(), where(part.getProperty().toDotPath()),
+ Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()),
iterator);
+ return base.and(criteria);
}
@@ -92,10 +94,9 @@ class MongoQueryCreator extends AbstractQueryCreator {
* #or(java.lang.Object, java.lang.Object)
*/
@Override
- protected Criteria or(Criteria base, Criteria criteria) {
-
- base.or(Collections.singletonList(new Query(criteria)));
- return base;
+ protected Query or(Query base, Query query) {
+
+ return new Query().or(base, query);
}
@@ -107,9 +108,7 @@ class MongoQueryCreator extends AbstractQueryCreator {
* #complete(java.lang.Object, org.springframework.data.domain.Sort)
*/
@Override
- protected Query complete(Criteria criteria, Sort sort) {
-
- Query query = new Query(criteria);
+ protected Query complete(Query query, Sort sort) {
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query.getQueryObject());
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
index f4c3be616..aafac0e8f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
@@ -179,4 +179,20 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result.size(), is(3));
assertThat(result, hasItems(dave, leroi, stefan));
}
+
+ @Test
+ public void findsPeopleByLastnameLikeAndAgeIn() throws Exception {
+
+ List result = repository.findByLastnameLikeAndAgeBetween("*e*", 44, 50);
+ assertThat(result.size(), is(2));
+ assertThat(result, hasItems(carter, boyd));
+ }
+
+ @Test
+ public void findsPeopleWithAndAndOr() throws Exception {
+
+ List result = repository.findByAgeOrLastnameLikeAndFirstnameLike(45, "*ss*", "*a*");
+ assertThat(result.size(), is(2));
+ assertThat(result, hasItems(boyd, stefan));
+ }
}
\ No newline at end of file
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java
index 0f9b73153..b0872dd3e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java
@@ -192,4 +192,12 @@ public class Person {
return id.hashCode();
}
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return String.format("%s %s", firstname, lastname);
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java
index 9eb934c69..232d40af7 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java
@@ -113,4 +113,8 @@ public interface PersonRepository extends MongoRepository {
List findByAddressZipCode(String zipCode);
+
+ List findByLastnameLikeAndAgeBetween(String lastname, int from, int to);
+
+ List findByAgeOrLastnameLikeAndFirstnameLike(int age, String lastname, String firstname);
}
From 358fd51c4d88db665ae3c65de2cc3d66ab11a858 Mon Sep 17 00:00:00 2001
From: Thomas Risberg
Date: Wed, 2 Mar 2011 08:58:36 -0500
Subject: [PATCH 2/9] DATADOC-48 adding basic cross-store features
---
spring-data-mongodb-cross-store/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 11 +-
.../document/MongoChangeSetPersister.java | 120 ++++++++++++++++++
.../document/MongoDocumentBacking.aj | 12 ++
.../document/MongoEntityOperations.java | 58 +++++++++
.../persistence/CrossStoreMongoTests.java | 5 +-
.../document/persistence/MongoPerson.java | 3 +
.../META-INF/spring/applicationContext.xml | 27 +++-
8 files changed, 229 insertions(+), 9 deletions(-)
create mode 100644 spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
create mode 100644 spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
create mode 100644 spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
diff --git a/spring-data-mongodb-cross-store/.classpath b/spring-data-mongodb-cross-store/.classpath
index 399cc9b91..35f990ccf 100644
--- a/spring-data-mongodb-cross-store/.classpath
+++ b/spring-data-mongodb-cross-store/.classpath
@@ -4,7 +4,7 @@
-
+
diff --git a/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs b/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs
index 2bafcc110..644302d5c 100644
--- a/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs
+++ b/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs
@@ -1,6 +1,9 @@
-#Mon Feb 28 16:26:01 EST 2011
+#Tue Mar 01 09:48:37 EST 2011
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
new file mode 100644
index 000000000..7af1aed73
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
@@ -0,0 +1,120 @@
+package org.springframework.persistence.document;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.DataAccessResourceFailureException;
+import org.springframework.persistence.support.ChangeSet;
+import org.springframework.persistence.support.ChangeSetBacked;
+import org.springframework.persistence.support.ChangeSetPersister;
+import org.springframework.util.ClassUtils;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.MongoException;
+
+//import edu.emory.mathcs.backport.java.util.Arrays;
+
+public class MongoChangeSetPersister implements ChangeSetPersister {
+
+ protected final Log log = LogFactory.getLog(getClass());
+
+ @Autowired
+ private DB mongoDb;
+
+ @Autowired
+ private ConversionService conversionService;
+
+ @Override
+ public void getPersistentState(Class extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
+ throws DataAccessException, NotFoundException {
+ String collection = ClassUtils.getQualifiedName(entityClass);
+ DBObject q = new BasicDBObject();
+ q.put("_id", id);
+ try {
+ DBObject dbo = mongoDb.getCollection(collection).findOne(q);
+ if (dbo == null) {
+ throw new NotFoundException();
+ }
+ String classShortName = ClassUtils.getShortName(entityClass);
+ for (Object property : dbo.toMap().keySet()) {
+ String propertyKey = (String) property;
+ String propertyName = propertyKey.startsWith(classShortName) ? propertyKey.substring(propertyKey.indexOf(classShortName)
+ + classShortName.length() + 1) : propertyKey;
+ // System.err.println("Mongo persisted property [" + propertyName + "] :: " + propertyKey + " = " + dbo.get(propertyKey));
+ if (propertyKey.startsWith("_")) {
+ // Id or class
+ changeSet.set(propertyName, dbo.get(propertyKey));
+ } else {
+ //throw new IllegalStateException("Unknown property [" + propertyName + "] found in MongoDB store");
+ changeSet.set(propertyName, dbo.get(propertyKey));
+ }
+ }
+ } catch (MongoException ex) {
+ throw new DataAccessResourceFailureException("Can't read from Mongo", ex);
+ }
+ }
+
+ @Override
+ public Object getPersistentId(Class extends ChangeSetBacked> entityClass,
+ ChangeSet cs) throws DataAccessException {
+ log.debug("getPersistentId called on " + entityClass);
+ if (cs == null) {
+ return null;
+ }
+ if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
+ // Not yet persistent
+ return null;
+ }
+ Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
+ return o;
+ }
+
+ @Override
+ public Object persistState(Class extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException {
+ log.info("PERSIST::"+cs);
+ cs.set(CLASS_KEY, entityClass.getName());
+ String idstr = cs.get(ID_KEY, String.class, this.conversionService);
+ Object id = null;
+ if (idstr != null) {
+ id = idstr;
+ }
+ if (id == null) {
+ log.info("Flush: entity make persistent; data store will assign id");
+ cs.set("_class", entityClass.getName());
+ String collection = entityClass.getName();
+ DBCollection dbc = mongoDb.getCollection(collection);
+ DBObject dbo = mapChangeSetToDbObject(cs);
+ if (dbc == null) {
+ dbc = mongoDb.createCollection(collection, dbo);
+ }
+ dbc.save(dbo);
+ id = dbo.get(ID_KEY);
+ } else {
+ log.info("Flush: entity already persistent with id=" + id);
+ String collection = entityClass.getName();
+ DBCollection dbc = mongoDb.getCollection(collection);
+ DBObject dbo = mapChangeSetToDbObject(cs);
+ if (dbc == null) {
+ throw new DataAccessResourceFailureException("Expected to find a collection named '" + collection +"'. It was not found, so ChangeSet can't be persisted.");
+ }
+ dbc.save(dbo);
+ }
+
+ return 0L;
+ }
+
+ private DBObject mapChangeSetToDbObject(ChangeSet cs) {
+ BasicDBObject dbo = new BasicDBObject();
+ for (String property : cs.getValues().keySet()) {
+ dbo.put(property, cs.getValues().get(property));
+ }
+ return dbo;
+ }
+}
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
new file mode 100644
index 000000000..914c1efcf
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
@@ -0,0 +1,12 @@
+package org.springframework.persistence.document;
+
+import org.springframework.persistence.support.AbstractDeferredUpdateMixinFields;
+
+/**
+ * Aspect to turn an object annotated with DocumentEntity into a document entity using Mongo.
+ *
+ * @author Thomas Risberg
+ */
+public aspect MongoDocumentBacking extends AbstractDeferredUpdateMixinFields {
+
+}
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
new file mode 100644
index 000000000..c812358da
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
@@ -0,0 +1,58 @@
+package org.springframework.persistence.document;
+
+import java.lang.reflect.Field;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.persistence.OrderedEntityOperations;
+import org.springframework.persistence.RelatedEntity;
+import org.springframework.persistence.support.ChangeSetBacked;
+
+import com.mongodb.DB;
+
+public class MongoEntityOperations extends OrderedEntityOperations {
+
+ @Autowired
+ private DB mongoDb;
+
+ @Autowired
+ private MongoChangeSetPersister changeSetPersister;
+
+ @Override
+ public boolean cacheInEntity() {
+ return true;
+ }
+
+ @Override
+ public ChangeSetBacked findEntity(Class entityClass, Object pk) throws DataAccessException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object findUniqueKey(ChangeSetBacked entity) throws DataAccessException {
+ return entity.getId();
+ }
+
+ @Override
+ public boolean isTransactional() {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public boolean isTransient(ChangeSetBacked entity) throws DataAccessException {
+ return entity.getId() == null;
+ }
+
+ @Override
+ public Object makePersistent(Object owner, ChangeSetBacked entity, Field f, RelatedEntity fs) throws DataAccessException {
+ changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
+ return entity.getId();
+ }
+
+ @Override
+ public boolean supports(Class> entityClass, RelatedEntity fs) {
+ return entityClass.isAnnotationPresent(DocumentEntity.class);
+ }
+
+}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
index 6cc76521d..c9e863383 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
@@ -19,12 +19,11 @@ public class CrossStoreMongoTests {
private Mongo mongo;
@Test
-// @Transactional
-// @Rollback(false)
+ @Transactional
+ @Rollback(false)
public void testUserConstructor() {
int age = 33;
MongoPerson p = new MongoPerson("Thomas", age);
- //Assert.assertEquals(p.getRedisValue().getString("RedisPerson.name"), p.getName());
Assert.assertEquals(age, p.getAge());
p.birthday();
Assert.assertEquals(1 + age, p.getAge());
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
index c1a32ee02..d1ad5e3f8 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
@@ -1,8 +1,11 @@
package org.springframework.data.document.persistence;
+import javax.persistence.Entity;
+
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.document.DocumentEntity;
+@Entity
@DocumentEntity
public class MongoPerson {
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
index 0e0d85184..4ab9c2012 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
@@ -1,7 +1,9 @@
+ xmlns:tx="http://www.springframework.org/schema/tx"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
@@ -16,4 +18,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 4d3db4fd4726058383064c0e7483c3beece8c725 Mon Sep 17 00:00:00 2001
From: Thomas Risberg
Date: Wed, 2 Mar 2011 09:57:12 -0500
Subject: [PATCH 3/9] DATADOC-48 adding basic cross-store features and some
tests
---
spring-data-mongodb-cross-store/.classpath | 4 +--
.../document/MongoChangeSetPersister.java | 15 ++++----
.../data/document/persistence/Account.java | 3 ++
.../document/persistence/MongoPerson.java | 3 --
.../test/resources/META-INF/persistence.xml | 13 +++++++
.../META-INF/spring/applicationContext.xml | 34 +++++++++++++------
.../src/test/resources/log4j.properties | 13 +++++++
7 files changed, 64 insertions(+), 21 deletions(-)
create mode 100644 spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
create mode 100644 spring-data-mongodb-cross-store/src/test/resources/log4j.properties
diff --git a/spring-data-mongodb-cross-store/.classpath b/spring-data-mongodb-cross-store/.classpath
index 35f990ccf..cad9eafec 100644
--- a/spring-data-mongodb-cross-store/.classpath
+++ b/spring-data-mongodb-cross-store/.classpath
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
index 7af1aed73..eb906d323 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
@@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
+import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.persistence.support.ChangeSet;
import org.springframework.persistence.support.ChangeSetBacked;
import org.springframework.persistence.support.ChangeSetPersister;
@@ -17,6 +18,7 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
+import com.mongodb.Mongo;
import com.mongodb.MongoException;
//import edu.emory.mathcs.backport.java.util.Arrays;
@@ -26,11 +28,11 @@ public class MongoChangeSetPersister implements ChangeSetPersister {
protected final Log log = LogFactory.getLog(getClass());
@Autowired
- private DB mongoDb;
+ private MongoTemplate mongoTemplate;
@Autowired
private ConversionService conversionService;
-
+
@Override
public void getPersistentState(Class extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
throws DataAccessException, NotFoundException {
@@ -38,7 +40,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister {
DBObject q = new BasicDBObject();
q.put("_id", id);
try {
- DBObject dbo = mongoDb.getCollection(collection).findOne(q);
+ DBObject dbo = mongoTemplate.getCollection(collection).findOne(q);
if (dbo == null) {
throw new NotFoundException();
}
@@ -89,17 +91,18 @@ public class MongoChangeSetPersister implements ChangeSetPersister {
log.info("Flush: entity make persistent; data store will assign id");
cs.set("_class", entityClass.getName());
String collection = entityClass.getName();
- DBCollection dbc = mongoDb.getCollection(collection);
+ DBCollection dbc = mongoTemplate.getCollection(collection);
DBObject dbo = mapChangeSetToDbObject(cs);
if (dbc == null) {
- dbc = mongoDb.createCollection(collection, dbo);
+ dbc = mongoTemplate.createCollection(collection);
}
dbc.save(dbo);
id = dbo.get(ID_KEY);
+ log.info("Data store assigned id: " + id);
} else {
log.info("Flush: entity already persistent with id=" + id);
String collection = entityClass.getName();
- DBCollection dbc = mongoDb.getCollection(collection);
+ DBCollection dbc = mongoTemplate.getCollection(collection);
DBObject dbo = mapChangeSetToDbObject(cs);
if (dbc == null) {
throw new DataAccessResourceFailureException("Expected to find a collection named '" + collection +"'. It was not found, so ChangeSet can't be persisted.");
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java
index 26edd39f3..87b882df2 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java
@@ -1,9 +1,12 @@
package org.springframework.data.document.persistence;
import javax.persistence.Entity;
+import javax.persistence.Id;
@Entity
public class Account {
+
+ @Id private Long id;
private String name;
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
index d1ad5e3f8..c1a32ee02 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
@@ -1,11 +1,8 @@
package org.springframework.data.document.persistence;
-import javax.persistence.Entity;
-
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.document.DocumentEntity;
-@Entity
@DocumentEntity
public class MongoPerson {
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
new file mode 100644
index 000000000..6415a90e8
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,13 @@
+
+
+
+ org.hibernate.ejb.HibernatePersistence
+ org.springframework.data.document.persistence.Account
+
+
+
+
+
+
+
+
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
index 4ab9c2012..da626d90e 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
@@ -2,7 +2,9 @@
@@ -11,9 +13,9 @@
-
-
-
+
+
+
@@ -23,13 +25,17 @@
factory-method="aspectOf">
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
@@ -38,7 +44,15 @@
+
+
+
+
+
+
+
+
diff --git a/spring-data-mongodb-cross-store/src/test/resources/log4j.properties b/spring-data-mongodb-cross-store/src/test/resources/log4j.properties
new file mode 100644
index 000000000..292bb1d4d
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/test/resources/log4j.properties
@@ -0,0 +1,13 @@
+log4j.rootCategory=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
+
+log4j.category.org.springframework=INFO
+log4j.category.org.springframework.data=DEBUG
+log4j.category.org.springframework.persistence=DEBUG
+
+log4j.category.org.hibernate.SQL=DEBUG
+# for debugging datasource initialization
+# log4j.category.test.jdbc=DEBUG
From 716875db03c7c2766eb44d55cc784568694d4c9a Mon Sep 17 00:00:00 2001
From: Thomas Risberg
Date: Wed, 2 Mar 2011 10:08:31 -0500
Subject: [PATCH 4/9] added cross-store to full build
---
pom.xml | 1 +
spring-data-mongodb-cross-store/pom.xml | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 667c3c198..10907b79f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,7 @@
spring-data-document-parent
spring-data-document-core
spring-data-mongodb
+ spring-data-mongodb-cross-store
spring-data-couchdb
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index ae18b6747..d80a94777 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -189,10 +189,10 @@
org.springframework
spring-aspects
-
+
1.6
1.6
From 8f39f6616d727be319df792e3d2c8e2b63de5849 Mon Sep 17 00:00:00 2001
From: Thomas Risberg
Date: Thu, 3 Mar 2011 10:16:54 -0500
Subject: [PATCH 5/9] DATADOC-48 some more cross-store tests
---
spring-data-mongodb-cross-store/.classpath | 2 +-
.../document/MongoChangeSetPersister.java | 5 +-
.../persistence/CrossStoreMongoTests.java | 30 ++++++++++
.../document/test}/Account.java | 2 +-
.../document/test}/MongoPerson.java | 2 +-
.../test/MongoPerson_Roo_Mongo_Entity.aj | 60 +++++++++++++++++++
.../test/resources/META-INF/persistence.xml | 2 +-
.../META-INF/spring/applicationContext.xml | 11 +++-
8 files changed, 107 insertions(+), 7 deletions(-)
rename spring-data-mongodb-cross-store/src/test/java/org/springframework/{data/document/persistence => persistence/document/test}/Account.java (93%)
rename spring-data-mongodb-cross-store/src/test/java/org/springframework/{data/document/persistence => persistence/document/test}/MongoPerson.java (95%)
create mode 100644 spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
diff --git a/spring-data-mongodb-cross-store/.classpath b/spring-data-mongodb-cross-store/.classpath
index cad9eafec..b1ef66a27 100644
--- a/spring-data-mongodb-cross-store/.classpath
+++ b/spring-data-mongodb-cross-store/.classpath
@@ -2,7 +2,7 @@
-
+
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
index eb906d323..e36f94a85 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
@@ -36,11 +36,12 @@ public class MongoChangeSetPersister implements ChangeSetPersister {
@Override
public void getPersistentState(Class extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
throws DataAccessException, NotFoundException {
- String collection = ClassUtils.getQualifiedName(entityClass);
+ String collection = ClassUtils.getShortName(entityClass).toLowerCase();
DBObject q = new BasicDBObject();
q.put("_id", id);
try {
DBObject dbo = mongoTemplate.getCollection(collection).findOne(q);
+ log.debug("Found DBObject: " + dbo);
if (dbo == null) {
throw new NotFoundException();
}
@@ -90,7 +91,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister {
if (id == null) {
log.info("Flush: entity make persistent; data store will assign id");
cs.set("_class", entityClass.getName());
- String collection = entityClass.getName();
+ String collection = entityClass.getSimpleName().toLowerCase();
DBCollection dbc = mongoTemplate.getCollection(collection);
DBObject dbo = mapChangeSetToDbObject(cs);
if (dbc == null) {
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
index c9e863383..8de689735 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
@@ -4,12 +4,18 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.document.mongodb.MongoTemplate;
+import org.springframework.persistence.document.test.MongoPerson;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
import com.mongodb.Mongo;
+import com.mongodb.MongoException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
@@ -18,6 +24,17 @@ public class CrossStoreMongoTests {
@Autowired
private Mongo mongo;
+ @Autowired
+ private MongoTemplate mongoTemplate;
+
+ @BeforeTransaction
+ public void setUp() {
+ DBCollection col = this.mongoTemplate.getCollection(MongoPerson.class.getSimpleName().toLowerCase());
+ if (col != null) {
+ this.mongoTemplate.dropCollection(MongoPerson.class.getName());
+ }
+ }
+
@Test
@Transactional
@Rollback(false)
@@ -29,4 +46,17 @@ public class CrossStoreMongoTests {
Assert.assertEquals(1 + age, p.getAge());
}
+ @Test
+ @Transactional
+ public void testInstantiatedFinder() throws MongoException {
+ String key = MongoPerson.class.getSimpleName().toLowerCase();
+ DBCollection col = this.mongoTemplate.getCollection(key);
+ DBObject dbo = col.findOne();
+ Object id1 = dbo.get("_id");
+ MongoPerson found = MongoPerson.findPerson(id1);
+ Assert.assertNotNull(found);
+ Assert.assertEquals(id1, found.getId());
+ System.out.println("Loaded MongoPerson data: " + found);
+ }
+
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
similarity index 93%
rename from spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java
rename to spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
index 87b882df2..f73a6586e 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/Account.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
@@ -1,4 +1,4 @@
-package org.springframework.data.document.persistence;
+package org.springframework.persistence.document.test;
import javax.persistence.Entity;
import javax.persistence.Id;
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
similarity index 95%
rename from spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
rename to spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
index c1a32ee02..0603beb86 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/MongoPerson.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
@@ -1,4 +1,4 @@
-package org.springframework.data.document.persistence;
+package org.springframework.persistence.document.test;
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.document.DocumentEntity;
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
new file mode 100644
index 000000000..3cd14ee18
--- /dev/null
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
@@ -0,0 +1,60 @@
+package org.springframework.persistence.document.test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.persistence.support.ChangeSet;
+import org.springframework.persistence.support.ChangeSetBacked;
+import org.springframework.persistence.support.ChangeSetConfiguration;
+import org.springframework.persistence.support.ChangeSetPersister;
+import org.springframework.persistence.support.ChangeSetSynchronizer;
+import org.springframework.persistence.support.HashMapChangeSet;
+import org.springframework.persistence.support.ChangeSetPersister.NotFoundException;
+
+/**
+ * EXAMPLE OF CODE THAT SHOULD BE GENERATED BY ROO BESIDES EACH MONGOENTITY CLASS
+ *
+ * Note: Combines X_Roo_Entity with X_Roo_Finder, as
+ * we need only a single aspect for entities.
+ *
+ * @author Thomas Risberg
+ *
+ */
+privileged aspect MongoPerson_Roo_Mongo_Entity {
+
+ private static ChangeSetPersister changeSetPersister() {
+ return new MongoConfigurationHolder().changeSetConfig.getChangeSetPersister();
+ }
+
+ private static ChangeSetSynchronizer changeSetManager() {
+ return new MongoConfigurationHolder().changeSetConfig.getChangeSetManager();
+ }
+
+ @Configurable
+ public static class MongoConfigurationHolder {
+ @Autowired
+ @Qualifier("mongoChangeSetConfiguration")
+ public ChangeSetConfiguration changeSetConfig;
+ }
+
+ /**
+ * Add constructor that takes ChangeSet.
+ * @param ChangeSet
+ */
+ public MongoPerson.new(ChangeSet cs) {
+ super();
+ setChangeSet(cs);
+ }
+
+ public static MongoPerson MongoPerson.findPerson(Object id) {
+ ChangeSet rv = new HashMapChangeSet();
+ try {
+ changeSetPersister().getPersistentState(MongoPerson.class, id, rv);
+ return new MongoPerson(rv);
+ }
+ catch (NotFoundException ex) {
+ return null;
+ }
+ }
+
+}
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
index 6415a90e8..232536c0d 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
@@ -2,7 +2,7 @@
org.hibernate.ejb.HibernatePersistence
- org.springframework.data.document.persistence.Account
+ org.springframework.persistence.document.test.Account
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
index da626d90e..bffad856c 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
@@ -3,10 +3,20 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
+ xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
+
+
+
+
+
+
+
+
@@ -15,7 +25,6 @@
-
From 9bc4f9ad98fe3f888dd2a5d6d4e9f54a97ad9390 Mon Sep 17 00:00:00 2001
From: Oliver Gierke
Date: Thu, 3 Mar 2011 18:06:36 +0100
Subject: [PATCH 6/9] Added fix for And and Or keywords to changelog.
---
src/main/resources/changelog.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt
index 19ef84cf4..3dd377881 100644
--- a/src/main/resources/changelog.txt
+++ b/src/main/resources/changelog.txt
@@ -7,6 +7,7 @@ Changes in version 1.0.0.M2 MongoDB
Repository
* Adapted new metamodel API (DATADOC-47, DATACMNS-17)
* Added support for 'In' and 'NotIn' keyword (DATADOC-46)
+* Fixed 'And' and 'Or' keywords
Changes in version 1.0.0.M1 MongoDB (2011-02-14)
------------------------------------------------
From 4616fb19a26f03e63868de6bad422047c18c71e0 Mon Sep 17 00:00:00 2001
From: Oliver Gierke
Date: Mon, 28 Feb 2011 14:30:42 +0100
Subject: [PATCH 7/9] DATADOC-34 - Create indexes for repository query methods
on start.
Adapted changes of Spring Data Commons. Implemented IndexEnsuringQueryCreationListener that creates an index for all the properties used in a query. Applies descending order by default but consideres potentially added OrderBy clauses in the query to define the order of the index attributes.
---
.../repository/AbstractMongoQuery.java | 260 +++++++-------
...adata.java => MongoEntityInformation.java} | 29 +-
.../mongodb/repository/MongoQueryMethod.java | 33 +-
.../MongoRepositoryFactoryBean.java | 68 +++-
.../repository/PartTreeMongoQuery.java | 9 +-
.../mongodb/repository/QueryUtils.java | 7 -
.../repository/SimpleMongoRepository.java | 323 +++++++++---------
.../MongoEntityMetadataUnitTests.java | 8 +-
8 files changed, 385 insertions(+), 352 deletions(-)
rename spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/{MongoEntityMetadata.java => MongoEntityInformation.java} (81%)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/AbstractMongoQuery.java
index f312d3049..617f538ba 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/AbstractMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/AbstractMongoQuery.java
@@ -26,7 +26,6 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.ParametersParameterAccessor;
-import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.util.Assert;
@@ -34,7 +33,6 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
-
/**
* Base class for {@link RepositoryQuery} implementations for Mongo.
*
@@ -42,167 +40,159 @@ import com.mongodb.DBObject;
*/
public abstract class AbstractMongoQuery implements RepositoryQuery {
- private final MongoQueryMethod method;
- private final MongoTemplate template;
+ private final MongoQueryMethod method;
+ private final MongoTemplate template;
+ /**
+ * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoTemplate}.
+ *
+ * @param method
+ * @param template
+ */
+ public AbstractMongoQuery(MongoQueryMethod method, MongoTemplate template) {
- /**
- * Creates a new {@link AbstractMongoQuery} from the given {@link QueryMethod} and
- * {@link MongoTemplate}.
- *
- * @param method
- * @param template
- */
- public AbstractMongoQuery(MongoQueryMethod method, MongoTemplate template) {
+ Assert.notNull(template);
+ Assert.notNull(method);
- Assert.notNull(template);
- Assert.notNull(method);
+ this.method = method;
+ this.template = template;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
+ */
+ public MongoQueryMethod getQueryMethod() {
+
+ return method;
+ }
- this.method = method;
- this.template = template;
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.query.RepositoryQuery#execute(java .lang.Object[])
+ */
+ public Object execute(Object[] parameters) {
+ ParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), parameters);
+ Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor));
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.query.RepositoryQuery#execute(java
- * .lang.Object[])
- */
- public Object execute(Object[] parameters) {
+ switch (method.getType()) {
+ case COLLECTION:
+ return new CollectionExecution().execute(query);
+ case PAGING:
+ return new PagedExecution(accessor.getPageable()).execute(query);
+ default:
+ return new SingleEntityExecution().execute(query);
+ }
+ }
- ParameterAccessor accessor =
- new ParametersParameterAccessor(method.getParameters(), parameters);
- Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor));
+ /**
+ * Create a {@link Query} instance using the given {@link ParameterAccessor}
+ * @param accessor
+ * @param converter
+ * @return
+ */
+ protected abstract Query createQuery(ConvertingParameterAccessor accessor);
- if (method.isCollectionQuery()) {
- return new CollectionExecution().execute(query);
- } else if (method.isPageQuery()) {
- return new PagedExecution(accessor.getPageable()).execute(query);
- } else {
- return new SingleEntityExecution().execute(query);
- }
- }
-
- /**
- * Create a {@link Query} instance using the given {@link ParameterAccessor}
- * @param accessor
- * @param converter
- * @return
- */
- protected abstract Query createQuery(ConvertingParameterAccessor accessor);
-
+ private abstract class Execution {
- private abstract class Execution {
+ abstract Object execute(Query query);
- abstract Object execute(Query query);
+ protected List> readCollection(Query query) {
+ MongoEntityInformation> metadata = method.getEntityMetadata();
- protected List> readCollection(Query query) {
+ String collectionName = metadata.getCollectionName();
+ return template.find(collectionName, query, metadata.getJavaType());
+ }
+ }
- String collectionName = getCollectionName(method.getDomainClass());
- return template
- .find(collectionName, query, method.getReturnedDomainClass());
- }
- }
+ /**
+ * {@link Execution} for collection returning queries.
+ *
+ * @author Oliver Gierke
+ */
+ class CollectionExecution extends Execution {
- /**
- * {@link Execution} for collection returning queries.
- *
- * @author Oliver Gierke
- */
- class CollectionExecution extends Execution {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.document.mongodb.repository.MongoQuery.Execution #execute(com.mongodb.DBObject)
+ */
+ @Override
+ public Object execute(Query query) {
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.document.mongodb.repository.MongoQuery.Execution
- * #execute(com.mongodb.DBObject)
- */
- @Override
- public Object execute(Query query) {
+ return readCollection(query);
+ }
+ }
- return readCollection(query);
- }
- }
+ /**
+ * {@link Execution} for pagination queries.
+ *
+ * @author Oliver Gierke
+ */
+ class PagedExecution extends Execution {
- /**
- * {@link Execution} for pagination queries.
- *
- * @author Oliver Gierke
- */
- class PagedExecution extends Execution {
+ private final Pageable pageable;
- private final Pageable pageable;
+ /**
+ * Creates a new {@link PagedExecution}.
+ *
+ * @param pageable
+ */
+ public PagedExecution(Pageable pageable) {
+ Assert.notNull(pageable);
+ this.pageable = pageable;
+ }
- /**
- * Creates a new {@link PagedExecution}.
- *
- * @param pageable
- */
- public PagedExecution(Pageable pageable) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.document.mongodb.repository.MongoQuery.Execution #execute(com.mongodb.DBObject)
+ */
+ @Override
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ Object execute(Query query) {
- Assert.notNull(pageable);
- this.pageable = pageable;
- }
+ MongoEntityInformation> metadata = method.getEntityMetadata();
+ int count = getCollectionCursor(metadata.getCollectionName(), query.getQueryObject()).count();
+ List> result = template.find(metadata.getCollectionName(), applyPagination(query, pageable),
+ metadata.getJavaType());
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.document.mongodb.repository.MongoQuery.Execution
- * #execute(com.mongodb.DBObject)
- */
- @Override
- @SuppressWarnings({ "rawtypes", "unchecked" })
- Object execute(Query query) {
+ return new PageImpl(result, pageable, count);
+ }
- String collectionName = getCollectionName(method.getDomainClass());
- int count = getCollectionCursor(collectionName, query.getQueryObject()).count();
+ private DBCursor getCollectionCursor(String collectionName, final DBObject query) {
- List> result =
- template.find(collectionName, applyPagination(query, pageable),
- method.getReturnedDomainClass());
+ return template.execute(collectionName, new CollectionCallback() {
- return new PageImpl(result, pageable, count);
- }
+ public DBCursor doInCollection(DBCollection collection) {
+ return collection.find(query);
+ }
+ });
+ }
+ }
- private DBCursor getCollectionCursor(String collectionName, final DBObject query) {
+ /**
+ * {@link Execution} to return a single entity.
+ *
+ * @author Oliver Gierke
+ */
+ class SingleEntityExecution extends Execution {
- return template.execute(collectionName, new CollectionCallback() {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.document.mongodb.repository.MongoQuery.Execution #execute(com.mongodb.DBObject)
+ */
+ @Override
+ Object execute(Query query) {
- public DBCursor doInCollection(DBCollection collection) {
-
- return collection.find(query);
- }
- });
- }
- }
-
- /**
- * {@link Execution} to return a single entity.
- *
- * @author Oliver Gierke
- */
- class SingleEntityExecution extends Execution {
-
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.document.mongodb.repository.MongoQuery.Execution
- * #execute(com.mongodb.DBObject)
- */
- @Override
- Object execute(Query query) {
-
- List> result = readCollection(query);
- return result.isEmpty() ? null : result.get(0);
- }
- }
+ List> result = readCollection(query);
+ return result.isEmpty() ? null : result.get(0);
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadata.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityInformation.java
similarity index 81%
rename from spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadata.java
rename to spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityInformation.java
index 439f6c2e2..1ace73e3e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadata.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoEntityInformation.java
@@ -19,8 +19,9 @@ import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
-import org.springframework.data.repository.support.AbstractEntityMetadata;
+import org.springframework.data.repository.support.AbstractEntityInformation;
import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
/**
@@ -29,18 +30,18 @@ import org.springframework.util.ReflectionUtils;
*
* @author Oliver Gierke
*/
-class MongoEntityMetadata extends AbstractEntityMetadata {
+class MongoEntityInformation extends AbstractEntityInformation {
private static final List FIELD_NAMES = Arrays.asList("ID", "id", "_id");
private Field field;
/**
- * Creates a new {@link MongoEntityMetadata}.
+ * Creates a new {@link MongoEntityInformation}.
*
* @param domainClass
*/
- public MongoEntityMetadata(Class domainClass) {
+ public MongoEntityInformation(Class domainClass) {
super(domainClass);
@@ -61,16 +62,16 @@ class MongoEntityMetadata extends AbstractEntityMetadata {
domainClass.getName()));
}
}
-
-
- /**
- * Returns the actual field name containing the id.
- *
- * @return
- */
- public String getFieldName() {
-
- return field.getName();
+
+
+ public String getCollectionName() {
+
+ return StringUtils.uncapitalize(getJavaType().getSimpleName());
+ }
+
+ public String getIdAttribute() {
+
+ return "_id";
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
index ec6777e0a..9d6da84b6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
@@ -31,7 +31,7 @@ import org.springframework.util.StringUtils;
class MongoQueryMethod extends QueryMethod {
private final Method method;
- private final Class> domainClass;
+ private final MongoEntityInformation> entityInformation;
/**
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
@@ -41,27 +41,9 @@ class MongoQueryMethod extends QueryMethod {
public MongoQueryMethod(Method method, Class> domainClass) {
super(method);
this.method = method;
- this.domainClass = domainClass;
+ this.entityInformation = new MongoEntityInformation(ClassUtils.getReturnedDomainClass(method));
}
-
- /* (non-Javadoc)
- * @see org.springframework.data.repository.query.QueryMethod#getDomainClass()
- */
- @Override
- public Class> getDomainClass() {
- return this.domainClass;
- }
-
-
- /**
- * Returns the type that will be returned by the query method.
- *
- * @return
- */
- public Class> getReturnedDomainClass() {
- return ClassUtils.getReturnedDomainClass(method);
- }
-
+
/**
* Returns whether the method has an annotated query.
@@ -95,6 +77,15 @@ class MongoQueryMethod extends QueryMethod {
return StringUtils.hasText(value) ? value : null;
}
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.query.QueryMethod#getEntityMetadata()
+ */
+ @Override
+ public MongoEntityInformation> getEntityMetadata() {
+
+ return entityInformation;
+ }
/**
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
index b03370f32..ca4be6d09 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
@@ -18,12 +18,20 @@ package org.springframework.data.document.mongodb.repository;
import java.io.Serializable;
import java.lang.reflect.Method;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor;
import org.springframework.data.document.mongodb.MongoTemplate;
+import org.springframework.data.document.mongodb.query.Index;
+import org.springframework.data.document.mongodb.query.Order;
+import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
-import org.springframework.data.repository.support.EntityMetadata;
+import org.springframework.data.repository.query.parser.Part;
+import org.springframework.data.repository.query.parser.PartTree;
+import org.springframework.data.repository.support.QueryCreationListener;
import org.springframework.data.repository.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.support.RepositoryFactorySupport;
import org.springframework.data.repository.support.RepositoryMetadata;
@@ -57,7 +65,9 @@ public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport info = new MongoEntityMetadata((Class) metadata.getDomainClass());
+ MongoEntityInformation info = new MongoEntityInformation(
+ (Class) metadata.getDomainClass());
return new SimpleMongoRepository(info, template);
}
@@ -152,4 +163,55 @@ public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(IndexEnsuringQueryCreationListener.class);
+ private final MongoOperations operations;
+
+ public IndexEnsuringQueryCreationListener(MongoOperations operations) {
+ this.operations = operations;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.QueryCreationListener#onCreation(org.springframework.data.repository
+ * .query.RepositoryQuery)
+ */
+ public void onCreation(PartTreeMongoQuery query) {
+
+ PartTree tree = query.getTree();
+ Index index = new Index();
+ index.named(query.getQueryMethod().getName());
+ Sort sort = tree.getSort();
+
+ for (Part part : tree.getParts()) {
+ String property = part.getProperty().toDotPath();
+ Order order = toOrder(sort, property);
+ index.on(property, order);
+ }
+
+ MongoEntityInformation> metadata = query.getQueryMethod().getEntityMetadata();
+ operations.ensureIndex(metadata.getCollectionName(), index);
+ LOG.debug(String.format("Created index %s!", index.toString()));
+ }
+
+ private static Order toOrder(Sort sort, String property) {
+
+ if (sort == null) {
+ return Order.DESCENDING;
+ }
+
+ org.springframework.data.domain.Sort.Order order = sort.getOrderFor(property);
+ return order == null ? Order.DESCENDING : order.isAscending() ? Order.ASCENDING : Order.DESCENDING;
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/PartTreeMongoQuery.java
index a9914874a..1ea20789e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/PartTreeMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/PartTreeMongoQuery.java
@@ -39,7 +39,14 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
public PartTreeMongoQuery(MongoQueryMethod method, MongoTemplate template) {
super(method, template);
- this.tree = new PartTree(method.getName(), method.getDomainClass());
+ this.tree = new PartTree(method.getName(), method.getEntityMetadata().getJavaType());
+ }
+
+ /**
+ * @return the tree
+ */
+ public PartTree getTree() {
+ return tree;
}
/*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java
index 3ca3eae66..39e6ba567 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryUtils.java
@@ -19,7 +19,6 @@ import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
-import org.springframework.util.StringUtils;
import com.mongodb.DBCursor;
@@ -84,10 +83,4 @@ abstract class QueryUtils {
return query;
}
-
-
- public static String getCollectionName(Class> domainClass) {
-
- return StringUtils.uncapitalize(domainClass.getSimpleName());
- }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java
index 56d107360..1d7b07544 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java
@@ -16,25 +16,23 @@
package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.query.Criteria.*;
-import static org.springframework.data.document.mongodb.repository.QueryUtils.*;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.bson.types.ObjectId;
-import org.springframework.data.document.mongodb.MongoConverter;
import org.springframework.data.document.mongodb.MongoTemplate;
+import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.PagingAndSortingRepository;
-import org.springframework.data.repository.support.EntityMetadata;
import org.springframework.util.Assert;
-
/**
* Repository base implementation for Mongo.
*
@@ -43,190 +41,181 @@ import org.springframework.util.Assert;
public class SimpleMongoRepository implements PagingAndSortingRepository {
private final MongoTemplate template;
- private final EntityMetadata entityInformation;
+ private final MongoEntityInformation entityInformation;
+
+ /**
+ * Creates a ew {@link SimpleMongoRepository} for the given {@link MongoInformation} and {@link MongoTemplate}.
+ *
+ * @param metadata
+ * @param template
+ */
+ public SimpleMongoRepository(MongoEntityInformation metadata, MongoTemplate template) {
+
+ Assert.notNull(template);
+ Assert.notNull(metadata);
+ this.entityInformation = metadata;
+ this.template = template;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#save(java.lang.Object)
+ */
+ public T save(T entity) {
+
+ template.save(entityInformation.getCollectionName(), entity);
+ return entity;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#save(java.lang.Iterable)
+ */
+ public List save(Iterable extends T> entities) {
+
+ List result = new ArrayList();
+
+ for (T entity : entities) {
+ save(entity);
+ result.add(entity);
+ }
+
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#findById(java.io.Serializable )
+ */
+ public T findById(ID id) {
+
+ return template.findOne(entityInformation.getCollectionName(), getIdQuery(id), entityInformation.getJavaType());
+ }
+
+ private Query getIdQuery(Object id) {
+
+ return new Query(getIdCriteria(id));
+ }
+
+ private Criteria getIdCriteria(Object id) {
+ ObjectId objectId = template.getConverter().convertObjectId(id);
+ return where(entityInformation.getIdAttribute()).is(objectId);
+ }
- /**
- * Creates a ew {@link SimpleMongoRepository} for the given domain class and
- * {@link MongoTemplate}.
- *
- * @param domainClass
- * @param template
- */
- public SimpleMongoRepository(EntityMetadata entityInformation, MongoTemplate template) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#exists(java.io.Serializable )
+ */
+ public boolean exists(ID id) {
- Assert.notNull(entityInformation);
- Assert.notNull(template);
- this.entityInformation = entityInformation;
- this.template = template;
- }
-
- private Class getDomainClass() {
- return entityInformation.getJavaType();
- }
+ return findById(id) != null;
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#count()
+ */
+ public Long count() {
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#save(java.lang.Object)
- */
- public T save(T entity) {
+ return template.getCollection(entityInformation.getCollectionName()).count();
+ }
- template.save(getCollectionName(getDomainClass()), entity);
- return entity;
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#delete(java.lang.Object)
+ */
+ public void delete(T entity) {
+ template.remove(entityInformation.getCollectionName(), getIdQuery(entityInformation.getId(entity)));
+ }
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#save(java.lang.Iterable)
- */
- public List save(Iterable extends T> entities) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#delete(java.lang.Iterable)
+ */
+ public void delete(Iterable extends T> entities) {
- List result = new ArrayList();
+ for (T entity : entities) {
+ delete(entity);
+ }
+ }
- for (T entity : entities) {
- save(entity);
- result.add(entity);
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#deleteAll()
+ */
+ public void deleteAll() {
- return result;
- }
+ template.dropCollection(entityInformation.getCollectionName());
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.Repository#findAll()
+ */
+ public List findAll() {
+ return findAll(new Query());
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.PagingAndSortingRepository#findAll
+ * (org.springframework.data.domain.Pageable)
+ */
+ public Page findAll(final Pageable pageable) {
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#findById(java.io.Serializable
- * )
- */
- public T findById(ID id) {
+ Long count = count();
+ List list = findAll(QueryUtils.applyPagination(new Query(), pageable));
- MongoConverter converter = template.getConverter();
- ObjectId objectId = converter.convertObjectId(id);
+ return new PageImpl(list, pageable, count);
+ }
- return template.findOne(getCollectionName(getDomainClass()), new Query(
- where("_id").is(objectId)), getDomainClass());
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.PagingAndSortingRepository#findAll
+ * (org.springframework.data.domain.Sort)
+ */
+ public List findAll(final Sort sort) {
+ return findAll(QueryUtils.applySorting(new Query(), sort));
+ }
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#exists(java.io.Serializable
- * )
- */
- public boolean exists(ID id) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.data.repository.Repository#findAll(java.lang.Iterable)
+ */
+ public List findAll(Iterable ids) {
- return findById(id) != null;
- }
+ Query query = null;
+ for (ID id : ids) {
+ if (query == null) {
+ query = getIdQuery(id);
+ } else {
+ query = new Query().or(getIdQuery(id));
+ }
+ }
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.Repository#findAll()
- */
- public List findAll() {
+ return findAll(query);
+ }
- return template.getCollection(getCollectionName(getDomainClass()),
- getDomainClass());
- }
+ private List findAll(Query query) {
+ if (query == null) {
+ return Collections.emptyList();
+ }
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.Repository#count()
- */
- public Long count() {
-
- return template.getCollection(getCollectionName(getDomainClass()))
- .count();
- }
-
-
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#delete(java.lang.Object)
- */
- public void delete(T entity) {
-
- Object id = entityInformation.getId(entity);
- ObjectId objectId = template.getConverter().convertObjectId(id);
-
- Query query =
- new Query(where("_id").is(
- objectId));
- template.remove(getCollectionName(getDomainClass()), query);
- }
-
-
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.Repository#delete(java.lang.Iterable)
- */
- public void delete(Iterable extends T> entities) {
-
- for (T entity : entities) {
- delete(entity);
- }
- }
-
-
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.Repository#deleteAll()
- */
- public void deleteAll() {
-
- template.dropCollection(getCollectionName(getDomainClass()));
- }
-
-
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.PagingAndSortingRepository#findAll
- * (org.springframework.data.domain.Pageable)
- */
- public Page findAll(final Pageable pageable) {
-
- Long count = count();
- Query spec = new Query();
-
- List list =
- template.find(getCollectionName(getDomainClass()),
- QueryUtils.applyPagination(spec, pageable),
- getDomainClass());
-
- return new PageImpl(list, pageable, count);
- }
-
-
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.PagingAndSortingRepository#findAll
- * (org.springframework.data.domain.Sort)
- */
- public List findAll(final Sort sort) {
-
- Query query = QueryUtils.applySorting(new Query(), sort);
- return template.find(getCollectionName(getDomainClass()), query,
- getDomainClass());
- }
+ return template.find(entityInformation.getCollectionName(), query, entityInformation.getJavaType());
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadataUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadataUnitTests.java
index 4ee90c0c8..7446377ab 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadataUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/MongoEntityMetadataUnitTests.java
@@ -22,7 +22,7 @@ import org.junit.Test;
/**
- * Unit test for {@link MongoEntityMetadata}.
+ * Unit test for {@link MongoEntityInformation}.
*
* @author Oliver Gierke
*/
@@ -31,8 +31,8 @@ public class MongoEntityMetadataUnitTests {
@Test
public void findsIdField() throws Exception {
- MongoEntityMetadata isNewAware =
- new MongoEntityMetadata(Person.class);
+ MongoEntityInformation isNewAware =
+ new MongoEntityInformation(Person.class);
Person person = new Person();
assertThat(isNewAware.isNew(person), is(true));
@@ -44,7 +44,7 @@ public class MongoEntityMetadataUnitTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsClassIfNoIdField() throws Exception {
- new MongoEntityMetadata(InvalidPerson.class);
+ new MongoEntityInformation(InvalidPerson.class);
}
class Person {
From 0baad6740a0e027f1e4ee073db445812cf69950f Mon Sep 17 00:00:00 2001
From: Mark Pollack
Date: Mon, 7 Mar 2011 14:54:12 -0500
Subject: [PATCH 8/9] Simple CouchDB integration
---
spring-data-couchdb/.classpath | 3 +
spring-data-couchdb/pom.xml | 70 +++-
.../CannotGetCouchDbConnectionException.java | 11 -
.../document/couchdb/CouchDbFactoryBean.java | 96 ------
.../CouchServerResourceUsageException.java | 33 ++
.../data/document/couchdb/CouchTemplate.java | 52 ---
.../document/couchdb/CouchUsageException.java | 35 ++
.../couchdb/DocumentExistsException.java | 34 ++
.../DocumentRetrievalFailureException.java | 38 ++
...UncategorizedCouchDataAccessException.java | 33 ++
.../document/couchdb/admin/CouchAdmin.java | 64 ++++
.../couchdb/admin/CouchAdminOperations.java | 34 ++
.../data/document/couchdb/admin/DbInfo.java | 77 +++++
.../couchdb/config/CouchJmxParser.java | 70 ++++
.../couchdb/config/CouchNamespaceHandler.java | 43 +++
.../couchdb/core/CouchOperations.java | 63 ++++
.../document/couchdb/core/CouchTemplate.java | 153 ++++++++
...hDbMappingJacksonHttpMessageConverter.java | 326 ++++++++++++++++++
.../couchdb/monitor/AbstractMonitor.java | 46 +++
.../document/couchdb/monitor/ServerInfo.java | 63 ++++
.../couchdb/monitor/package-info.java | 4 +
.../CouchUtils.java} | 49 ++-
.../main/resources/META-INF/spring.handlers | 1 +
.../main/resources/META-INF/spring.schemas | 2 +
.../main/resources/META-INF/spring.tooling | 4 +
.../couchdb/config/spring-couch-1.0.xsd | 33 ++
.../data/document/couchdb/DummyDocument.java | 77 +++++
.../data/document/couchdb/IsBodyEqual.java | 51 +++
.../admin/CouchAdminIntegrationTests.java | 37 ++
...AbstractCouchTemplateIntegrationTests.java | 124 +++++++
.../document/couchdb/core/CouchConstants.java | 27 ++
.../core/CouchTemplateIntegrationTests.java | 41 +++
.../couchdb/core/CouchTemplateTests.java | 32 ++
.../document/couchdb/monitor/JmxServer.java | 36 ++
.../src/test/resources/log4j.properties | 13 +
.../src/test/resources/server-jmx.xml | 25 ++
spring-data-couchdb/template.mf | 7 +-
spring-data-document-parent/pom.xml | 1 +
.../mongodb/analytics/MvcAnalyticsTests.java | 2 +
39 files changed, 1730 insertions(+), 180 deletions(-)
delete mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CannotGetCouchDbConnectionException.java
delete mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbFactoryBean.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchServerResourceUsageException.java
delete mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchTemplate.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchUsageException.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentExistsException.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentRetrievalFailureException.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/UncategorizedCouchDataAccessException.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdmin.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdminOperations.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/DbInfo.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchJmxParser.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchNamespaceHandler.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchOperations.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchTemplate.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/support/CouchDbMappingJacksonHttpMessageConverter.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
create mode 100644 spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/package-info.java
rename spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/{CouchDbUtils.java => support/CouchUtils.java} (61%)
create mode 100644 spring-data-couchdb/src/main/resources/META-INF/spring.handlers
create mode 100644 spring-data-couchdb/src/main/resources/META-INF/spring.schemas
create mode 100644 spring-data-couchdb/src/main/resources/META-INF/spring.tooling
create mode 100644 spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
create mode 100644 spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
create mode 100644 spring-data-couchdb/src/test/resources/log4j.properties
create mode 100644 spring-data-couchdb/src/test/resources/server-jmx.xml
diff --git a/spring-data-couchdb/.classpath b/spring-data-couchdb/.classpath
index 0bb7ad5ca..2064dbb9b 100644
--- a/spring-data-couchdb/.classpath
+++ b/spring-data-couchdb/.classpath
@@ -1,6 +1,9 @@
+
+
+
diff --git a/spring-data-couchdb/pom.xml b/spring-data-couchdb/pom.xml
index e9fbd9cbb..6914e22c0 100644
--- a/spring-data-couchdb/pom.xml
+++ b/spring-data-couchdb/pom.xml
@@ -10,6 +10,43 @@
spring-data-couchdb
jar
Spring Data CouchDB Support
+
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+ tareq.abedrabbo
+ Tareq Abedrabbo
+ tareq.abedrabbo@opencredo.com
+ OpenCredo
+ http://www.opencredo.org
+
+ Project Admin
+ Developer
+
+ +0
+
+
+ tomas.lukosius
+ Tomas Lukosius
+ tomas.lukosius@opencredo.com
+ OpenCredo
+ http://www.opencredo.org
+
+ Project Admin
+ Developer
+
+ +0
+
+
+
@@ -21,6 +58,12 @@
org.springframework
spring-tx
+
+
+ org.springframework
+ spring-test
+ test
+
@@ -28,6 +71,18 @@
spring-data-document-core
+
+
+ org.codehaus.jackson
+ jackson-core-asl
+ 1.6.1
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.6.1
+
+
org.slf4j
@@ -82,16 +137,18 @@
junit
junit
+ test
-
-
+
- com.google.code.jcouchdb
- jcouchdb
- 0.11.0-1
-
+ org.hamcrest
+ hamcrest-all
+ 1.1
+ test
+
+
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CannotGetCouchDbConnectionException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CannotGetCouchDbConnectionException.java
deleted file mode 100644
index a2fb40054..000000000
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CannotGetCouchDbConnectionException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.springframework.data.document.couchdb;
-
-import org.springframework.dao.DataAccessResourceFailureException;
-
-public class CannotGetCouchDbConnectionException extends DataAccessResourceFailureException {
-
- public CannotGetCouchDbConnectionException(String msg, Throwable cause) {
- super(msg, cause);
- }
-
-}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbFactoryBean.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbFactoryBean.java
deleted file mode 100644
index e243dee4c..000000000
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbFactoryBean.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2010 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.couchdb;
-
-import org.jcouchdb.db.Database;
-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.Assert;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Convenient factory for configuring MongoDB.
- *
- * @author Thomas Risberg
- * @since 1.0
- */
-public class CouchDbFactoryBean implements FactoryBean, InitializingBean,
- PersistenceExceptionTranslator {
-
- /**
- * Logger, available to subclasses.
- */
- protected final Log logger = LogFactory.getLog(getClass());
-
- private String host;
- private Integer port;
- private String databaseName;
-
- public void setDatabaseName(String databaseName) {
- this.databaseName = databaseName;
- }
-
- public void setHost(String host) {
- this.host = host;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public Database getObject() throws Exception {
- Assert.hasText(host, "Host must not be empty");
- Assert.hasText(databaseName, "Database name must not be empty");
- if (port == null) {
- return new Database(host, databaseName);
- }
- else {
- return new Database(host, port, databaseName);
- }
- }
-
- public Class extends Database> getObjectType() {
- return Database.class;
- }
-
- public boolean isSingleton() {
- return false;
- }
-
- public void afterPropertiesSet() throws Exception {
- // apply defaults - convenient when used to configure for tests
- // in an application context
- if (host == null) {
- logger.warn("Property host not specified. Using default 'localhost'");
- databaseName = "localhost";
- }
- if (databaseName == null) {
- logger.warn("Property databaseName not specified. Using default name 'test'");
- databaseName = "test";
- }
- }
-
- public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
- logger.debug("Translating " + ex);
- return CouchDbUtils.translateCouchExceptionIfPossible(ex);
- }
-
-}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchServerResourceUsageException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchServerResourceUsageException.java
new file mode 100644
index 000000000..ce8dee340
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchServerResourceUsageException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.couchdb;
+
+import org.springframework.dao.InvalidDataAccessResourceUsageException;
+import org.springframework.web.client.HttpServerErrorException;
+
+public class CouchServerResourceUsageException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Create a new CouchServerResourceUsageException,
+ * wrapping an arbitrary HttpServerErrorException.
+ * @param cause the HttpServerErrorException thrown
+ */
+ public CouchServerResourceUsageException(HttpServerErrorException cause) {
+ super(cause != null ? cause.getMessage() : null, cause);
+ }
+
+}
+
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchTemplate.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchTemplate.java
deleted file mode 100644
index 0e646ab02..000000000
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchTemplate.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2010 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.couchdb;
-
-
-import org.jcouchdb.db.Database;
-import org.jcouchdb.document.BaseDocument;
-import org.springframework.data.document.AbstractDocumentStoreTemplate;
-
- public class CouchTemplate extends AbstractDocumentStoreTemplate {
-
- private Database database;
-
- public CouchTemplate() {
- super();
- }
-
- public CouchTemplate(String host, String databaseName) {
- super();
- database = new Database(host, databaseName);
- }
-
- public CouchTemplate(Database database) {
- super();
- this.database = database;
- }
-
- public void save(BaseDocument document) {
- getConnection().createDocument(document);
- }
-
- @Override
- public Database getConnection() {
- return database;
- }
-
-
-}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchUsageException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchUsageException.java
new file mode 100644
index 000000000..95f17ef92
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchUsageException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.couchdb;
+
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
+
+public class CouchUsageException extends InvalidDataAccessApiUsageException
+ {
+
+ /**
+ * Create a new CouchUsageException,
+ * wrapping an arbitrary HttpServerErrorException.
+ * @param cause the HttpServerErrorException thrown
+ */
+ public CouchUsageException(HttpClientErrorException cause) {
+ super(cause != null ? cause.getMessage() : null, cause);
+ }
+
+}
+
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentExistsException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentExistsException.java
new file mode 100644
index 000000000..d564211a8
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentExistsException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.couchdb;
+
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.web.client.HttpStatusCodeException;
+
+public class DocumentExistsException extends DataIntegrityViolationException
+ {
+
+ /**
+ * Create a new DocumentExistsException,
+ * wrapping an arbitrary HttpServerErrorException.
+ * @param cause the HttpServerErrorException thrown
+ */
+ public DocumentExistsException(String documentId, HttpStatusCodeException cause) {
+ super(cause != null ? cause.getMessage() : null, cause);
+ }
+
+}
+
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentRetrievalFailureException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentRetrievalFailureException.java
new file mode 100644
index 000000000..c35a1ff6d
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/DocumentRetrievalFailureException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.couchdb;
+
+import org.springframework.dao.DataRetrievalFailureException;
+
+public class DocumentRetrievalFailureException extends
+ DataRetrievalFailureException {
+
+ private String documentPath;
+
+ public DocumentRetrievalFailureException(String documentPath) {
+ super("Could not find document at path = " + documentPath);
+ this.documentPath = documentPath;
+ }
+
+ public String getDocumentPath() {
+ return documentPath;
+ }
+
+
+
+
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/UncategorizedCouchDataAccessException.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/UncategorizedCouchDataAccessException.java
new file mode 100644
index 000000000..d951e5353
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/UncategorizedCouchDataAccessException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.couchdb;
+
+import org.springframework.dao.UncategorizedDataAccessException;
+import org.springframework.web.client.RestClientException;
+
+public class UncategorizedCouchDataAccessException extends UncategorizedDataAccessException {
+
+ /**
+ * Create a new HibernateSystemException,
+ * wrapping an arbitrary HibernateException.
+ * @param cause the HibernateException thrown
+ */
+ public UncategorizedCouchDataAccessException(RestClientException cause) {
+ super(cause != null ? cause.getMessage() : null, cause);
+ }
+
+}
+
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdmin.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdmin.java
new file mode 100644
index 000000000..f9c0eb62d
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdmin.java
@@ -0,0 +1,64 @@
+/*
+ * 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.couchdb.admin;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.document.couchdb.support.CouchUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+public class CouchAdmin implements CouchAdminOperations {
+
+ private String databaseUrl;
+ private RestOperations restOperations = new RestTemplate();
+
+ public CouchAdmin(String databaseUrl) {
+
+ if (!databaseUrl.trim().endsWith("/")) {
+ this.databaseUrl = databaseUrl.trim() + "/";
+ } else {
+ this.databaseUrl = databaseUrl.trim();
+ }
+ }
+
+ public List listDatabases() {
+ String dbs = restOperations.getForObject(databaseUrl + "_all_dbs", String.class);
+ return Arrays.asList(StringUtils.commaDelimitedListToStringArray(dbs));
+ }
+
+ public void createDatabase(String dbName) {
+ org.springframework.util.Assert.hasText(dbName);
+ restOperations.put(databaseUrl + dbName, null);
+
+ }
+
+ public void deleteDatabase(String dbName) {
+ org.springframework.util.Assert.hasText(dbName);
+ restOperations.delete(CouchUtils.ensureTrailingSlash(databaseUrl + dbName));
+
+ }
+
+ public DbInfo getDatabaseInfo(String dbName) {
+ String url = CouchUtils.ensureTrailingSlash(databaseUrl + dbName);
+ Map dbInfoMap = (Map) restOperations.getForObject(url, Map.class);
+ return new DbInfo(dbInfoMap);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdminOperations.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdminOperations.java
new file mode 100644
index 000000000..4c00dd68e
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/CouchAdminOperations.java
@@ -0,0 +1,34 @@
+/*
+ * 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.couchdb.admin;
+
+import java.util.List;
+
+public interface CouchAdminOperations {
+
+
+ // functionality for /_special - replication, logs, UUIDs
+
+ List listDatabases();
+
+ void createDatabase(String name);
+
+ void deleteDatabase(String name);
+
+ DbInfo getDatabaseInfo(String name);
+
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/DbInfo.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/DbInfo.java
new file mode 100644
index 000000000..d8aa62b42
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/admin/DbInfo.java
@@ -0,0 +1,77 @@
+/*
+ * 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.couchdb.admin;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class DbInfo {
+
+ private Map dbInfoMap;
+
+ public DbInfo(Map dbInfoMap) {
+ super();
+ this.dbInfoMap = dbInfoMap;
+ }
+
+ public boolean isCompactRunning() {
+ return (Boolean) this.dbInfoMap.get("compact_running");
+ }
+
+ public String getDbName() {
+ return (String) this.dbInfoMap.get("db_name");
+ }
+
+ public long getDiskFormatVersion() {
+ return (Long) this.dbInfoMap.get("disk_format_version");
+ }
+
+ public long getDiskSize() {
+ return (Long) this.dbInfoMap.get("disk_size");
+ }
+
+ public long getDocCount() {
+ return (Long) this.dbInfoMap.get("doc_count");
+ }
+
+ public long getDocDeleteCount() {
+ return (Long) this.dbInfoMap.get("doc_del_count");
+ }
+
+ public long getInstanceStartTime() {
+ return (Long) this.dbInfoMap.get("instance_start_time");
+ }
+
+ public long getPurgeSequence() {
+ return (Long) this.dbInfoMap.get("purge_seq");
+ }
+
+ public long getUpdateSequence() {
+ return (Long) this.dbInfoMap.get("update_seq");
+ }
+
+ public Map getDbInfoMap() {
+ return Collections.unmodifiableMap(dbInfoMap);
+ }
+
+
+
+
+
+
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchJmxParser.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchJmxParser.java
new file mode 100644
index 000000000..4521dc838
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchJmxParser.java
@@ -0,0 +1,70 @@
+/*
+ * 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.couchdb.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.document.couchdb.monitor.ServerInfo;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+public class CouchJmxParser implements BeanDefinitionParser {
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ String databaseUrl = element.getAttribute("database-url");
+ if (!StringUtils.hasText(databaseUrl)) {
+ databaseUrl = "http://localhost:5984";
+ }
+ registerJmxComponents(databaseUrl, element, parserContext);
+ return null;
+ }
+
+ protected void registerJmxComponents(String databaseUrl, Element element, ParserContext parserContext) {
+ Object eleSource = parserContext.extractSource(element);
+
+ CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
+
+ /*
+ createBeanDefEntry(AssertMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(BackgroundFlushingMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(BtreeIndexCounters.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(ConnectionMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(GlobalLockMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(MemoryMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
+ createBeanDefEntry(OperationCounters.class, compositeDef, mongoRefName, eleSource, parserContext);
+ */
+ createBeanDefEntry(ServerInfo.class, compositeDef, databaseUrl, eleSource, parserContext);
+ //createBeanDefEntry(MongoAdmin.class, compositeDef, mongoRefName, eleSource, parserContext);
+
+
+ parserContext.registerComponent(compositeDef);
+
+ }
+
+ protected void createBeanDefEntry(Class clazz, CompositeComponentDefinition compositeDef, String databaseUrl, Object eleSource, ParserContext parserContext) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
+ builder.getRawBeanDefinition().setSource(eleSource);
+ builder.addConstructorArg(databaseUrl);
+ BeanDefinition assertDef = builder.getBeanDefinition();
+ String assertName = parserContext.getReaderContext().registerWithGeneratedName(assertDef);
+ compositeDef.addNestedComponent(new BeanComponentDefinition(assertDef, assertName));
+ }
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchNamespaceHandler.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchNamespaceHandler.java
new file mode 100644
index 000000000..d47e1c76b
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/config/CouchNamespaceHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.couchdb.config;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+
+/**
+ * {@link org.springframework.beans.factory.xml.NamespaceHandler} for Couch DB
+ * based repositories.
+ *
+ * @author Oliver Gierke
+ */
+public class CouchNamespaceHandler extends NamespaceHandlerSupport {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.beans.factory.xml.NamespaceHandler#init()
+ */
+ public void init() {
+
+ /*
+ registerBeanDefinitionParser("repositories",
+ new MongoRepositoryConfigDefinitionParser());
+ */
+
+ registerBeanDefinitionParser("jmx", new CouchJmxParser());
+ }
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchOperations.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchOperations.java
new file mode 100644
index 000000000..b1573dc00
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchOperations.java
@@ -0,0 +1,63 @@
+/*
+ * 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.couchdb.core;
+
+import java.net.URI;
+
+
+public interface CouchOperations {
+
+ /**
+ * Reads a document from the database and maps it a Java object.
+ *
+ * This method is intended to work when a default database
+ * is set on the CouchDbDocumentOperations instance.
+ *
+ * @param id the id of the CouchDB document to read
+ * @param targetClass the target type to map to
+ * @return the mapped object
+ */
+ T findOne(String id, Class targetClass);
+
+ /**
+ * Reads a document from the database and maps it a Java object.
+ *
+ * @param uri the full URI of the document to read
+ * @param targetClass the target type to map to
+ * @return the mapped object
+ */
+ T findOne(URI uri, Class targetClass);
+
+
+ /**
+ * Maps a Java object to JSON and writes it to the database
+ *
+ * This method is intended to work when a default database
+ * is set on the CouchDbDocumentOperations instance.
+ *
+ * @param id the id of the document to write
+ * @param document the object to write
+ */
+ void save(String id, Object document);
+
+ /**
+ * Maps a Java object to JSON and writes it to the database
+ *
+ * @param uri the full URI of the document to write
+ * @param document the object to write
+ */
+ void save(URI uri, Object document);
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchTemplate.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchTemplate.java
new file mode 100644
index 000000000..8cb7014ec
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/CouchTemplate.java
@@ -0,0 +1,153 @@
+/*
+ * 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.couchdb.core;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.data.document.couchdb.CouchServerResourceUsageException;
+import org.springframework.data.document.couchdb.CouchUsageException;
+import org.springframework.data.document.couchdb.DocumentRetrievalFailureException;
+import org.springframework.data.document.couchdb.UncategorizedCouchDataAccessException;
+import org.springframework.data.document.couchdb.support.CouchUtils;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.Assert;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+
+public class CouchTemplate implements CouchOperations {
+
+ protected final Log logger = LogFactory.getLog(this.getClass());
+
+ private String defaultDocumentUrl;
+
+ private RestOperations restOperations = new RestTemplate();
+
+ /**
+ * Constructs an instance of CouchDbDocumentTemplate with a default database
+ * @param defaultDatabaseUrl the default database to connect to
+ */
+ public CouchTemplate(String defaultDatabaseUrl) {
+ Assert.hasText(defaultDatabaseUrl, "defaultDatabaseUrl must not be empty");
+ defaultDocumentUrl = CouchUtils.addId(defaultDatabaseUrl);
+ }
+
+ /**
+ * Constructs an instance of CouchDbDocumentTemplate with a default database
+ * @param defaultDatabaseUrl the default database to connect to
+ */
+ public CouchTemplate(String defaultDatabaseUrl, RestOperations restOperations) {
+ this(defaultDatabaseUrl);
+ Assert.notNull(restOperations, "restOperations must not be null");
+ this.restOperations = restOperations;
+ }
+
+
+
+
+
+ public T findOne(String id, Class targetClass) {
+ Assert.state(defaultDocumentUrl != null, "defaultDatabaseUrl must be set to use this method");
+ try {
+ return restOperations.getForObject(defaultDocumentUrl, targetClass, id);
+ //TODO check this exception translation and centralize.
+ } catch (HttpClientErrorException clientError) {
+ if (clientError.getStatusCode() == HttpStatus.NOT_FOUND) {
+ throw new DocumentRetrievalFailureException(defaultDocumentUrl + "/" + id);
+ }
+ throw new CouchUsageException(clientError);
+ } catch (HttpServerErrorException serverError) {
+ throw new CouchServerResourceUsageException(serverError);
+ } catch (RestClientException otherError) {
+ throw new UncategorizedCouchDataAccessException(otherError);
+ }
+ }
+
+ public T findOne(URI uri, Class targetClass) {
+ Assert.state(uri != null, "uri must be set to use this method");
+ try {
+ return restOperations.getForObject(uri, targetClass);
+ //TODO check this exception translation and centralize.
+ } catch (HttpClientErrorException clientError) {
+ if (clientError.getStatusCode() == HttpStatus.NOT_FOUND) {
+ throw new DocumentRetrievalFailureException(uri.getPath());
+ }
+ throw new CouchUsageException(clientError);
+ } catch (HttpServerErrorException serverError) {
+ throw new CouchServerResourceUsageException(serverError);
+ } catch (RestClientException otherError) {
+ throw new UncategorizedCouchDataAccessException(otherError);
+ }
+ }
+
+ public void save(String id, Object document) {
+ Assert.notNull(document, "document must not be null for save");
+ HttpEntity> httpEntity = createHttpEntity(document);
+ try {
+ ResponseEntity response = restOperations.exchange(defaultDocumentUrl, HttpMethod.PUT, httpEntity, Map.class, id);
+ //TODO update the document revision id on the object from the returned value
+ //TODO better exception translation
+ } catch (RestClientException e) {
+ throw new UncategorizedCouchDataAccessException(e);
+ }
+
+ }
+
+ public void save(URI uri, Object document) {
+ Assert.notNull(document, "document must not be null for save");
+ Assert.notNull(uri, "URI must not be null for save");
+ HttpEntity> httpEntity = createHttpEntity(document);
+ try {
+ ResponseEntity response = restOperations.exchange(uri, HttpMethod.PUT, httpEntity, Map.class);
+ //TODO update the document revision id on the object from the returned value
+ //TODO better exception translation
+ } catch (RestClientException e) {
+ throw new UncategorizedCouchDataAccessException(e);
+ }
+
+
+ }
+
+ private HttpEntity> createHttpEntity(Object document) {
+
+ if (document instanceof HttpEntity) {
+ HttpEntity httpEntity = (HttpEntity) document;
+ Assert.isTrue(httpEntity.getHeaders().getContentType().equals(MediaType.APPLICATION_JSON),
+ "HttpEntity payload with non application/json content type found.");
+ return httpEntity;
+ }
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ HttpEntity httpEntity = new HttpEntity(document, httpHeaders);
+
+ return httpEntity;
+ }
+
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/support/CouchDbMappingJacksonHttpMessageConverter.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/support/CouchDbMappingJacksonHttpMessageConverter.java
new file mode 100644
index 000000000..b49bfa09e
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/core/support/CouchDbMappingJacksonHttpMessageConverter.java
@@ -0,0 +1,326 @@
+/*
+ * 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.couchdb.core.support;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonEncoding;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.type.TypeFactory;
+import org.codehaus.jackson.type.JavaType;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.AbstractHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.util.Assert;
+
+public class CouchDbMappingJacksonHttpMessageConverter extends
+ AbstractHttpMessageConverter {
+
+ public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+ private static final String ROWS_FIELD_NAME = "rows";
+ private static final String VALUE_FIELD_NAME = "value";
+ private static final String INCLUDED_DOC_FIELD_NAME = "doc";
+ private static final String TOTAL_ROWS_FIELD_NAME = "total_rows";
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ private boolean prefixJson = false;
+
+ /**
+ * Construct a new {@code BindingJacksonHttpMessageConverter}.
+ */
+ public CouchDbMappingJacksonHttpMessageConverter() {
+ super(new MediaType("application", "json", DEFAULT_CHARSET));
+ }
+
+ /**
+ * Sets the {@code ObjectMapper} for this view. If not set, a default
+ * {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
+ *
+ * Setting a custom-configured {@code ObjectMapper} is one way to take
+ * further control of the JSON serialization process. For example, an
+ * extended {@link org.codehaus.jackson.map.SerializerFactory} can be
+ * configured that provides custom serializers for specific types. The other
+ * option for refining the serialization process is to use Jackson's
+ * provided annotations on the types to be serialized, in which case a
+ * custom-configured ObjectMapper is unnecessary.
+ */
+ public void setObjectMapper(ObjectMapper objectMapper) {
+ Assert.notNull(objectMapper, "'objectMapper' must not be null");
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * Indicates whether the JSON output by this view should be prefixed with
+ * "{} &&". Default is false.
+ *
+ * Prefixing the JSON string in this manner is used to help prevent JSON
+ * Hijacking. The prefix renders the string syntactically invalid as a
+ * script so that it cannot be hijacked. This prefix does not affect the
+ * evaluation of JSON, but if JSON validation is performed on the string,
+ * the prefix would need to be ignored.
+ */
+ public void setPrefixJson(boolean prefixJson) {
+ this.prefixJson = prefixJson;
+ }
+
+ @Override
+ public boolean canRead(Class> clazz, MediaType mediaType) {
+ JavaType javaType = getJavaType(clazz);
+ return this.objectMapper.canDeserialize(javaType) && canRead(mediaType);
+ }
+
+ /**
+ * Returns the Jackson {@link JavaType} for the specific class.
+ *
+ *
+ * Default implementation returns
+ * {@link TypeFactory#type(java.lang.reflect.Type)}, but this can be
+ * overridden in subclasses, to allow for custom generic collection
+ * handling. For instance:
+ *
+ *
+ * protected JavaType getJavaType(Class<?> clazz) {
+ * if (List.class.isAssignableFrom(clazz)) {
+ * return TypeFactory.collectionType(ArrayList.class, MyBean.class);
+ * } else {
+ * return super.getJavaType(clazz);
+ * }
+ * }
+ *
+ *
+ * @param clazz
+ * the class to return the java type for
+ * @return the java type
+ */
+ protected JavaType getJavaType(Class> clazz) {
+ return TypeFactory.type(clazz);
+ }
+
+ @Override
+ public boolean canWrite(Class> clazz, MediaType mediaType) {
+ return this.objectMapper.canSerialize(clazz) && canWrite(mediaType);
+ }
+
+ @Override
+ protected boolean supports(Class> clazz) {
+ // should not be called, since we override canRead/Write instead
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Object readInternal(Class> clazz, HttpInputMessage inputMessage)
+ throws IOException, HttpMessageNotReadableException {
+ JavaType javaType = getJavaType(clazz);
+ try {
+ return success(clazz, inputMessage);
+
+ // return this.objectMapper.readValue(inputMessage.getBody(),
+ // javaType);
+ } catch (Exception ex) {
+ throw new HttpMessageNotReadableException("Could not read JSON: "
+ + ex.getMessage(), ex);
+ }
+ }
+
+ private Object success(Class> clazz, HttpInputMessage inputMessage)
+ throws JsonParseException, IOException {
+
+ //Note, parsing code used from ektorp project
+ JsonParser jp = objectMapper.getJsonFactory().createJsonParser(
+ inputMessage.getBody());
+ if (jp.nextToken() != JsonToken.START_OBJECT) {
+ throw new RuntimeException("Expected data to start with an Object");
+ }
+ Map fields = readHeaderFields(jp);
+
+ List result;
+ if (fields.containsKey(TOTAL_ROWS_FIELD_NAME)) {
+ int totalRows = fields.get(TOTAL_ROWS_FIELD_NAME);
+ if (totalRows == 0) {
+ return Collections.emptyList();
+ }
+ result = new ArrayList(totalRows);
+ } else {
+ result = new ArrayList();
+ }
+
+ ParseState state = new ParseState();
+
+ Object first = parseFirstRow(jp, state, clazz);
+ if (first == null) {
+ return Collections.emptyList();
+ } else {
+ result.add(first);
+ }
+
+ while (jp.getCurrentToken() != null) {
+ skipToField(jp, state.docFieldName, state);
+ if (atEndOfRows(jp)) {
+ return result;
+ }
+ result.add(jp.readValueAs(clazz));
+ endRow(jp, state);
+ }
+ return result;
+ }
+
+ private Object parseFirstRow(JsonParser jp, ParseState state, Class clazz)
+ throws JsonParseException, IOException, JsonProcessingException,
+ JsonMappingException {
+ skipToField(jp, VALUE_FIELD_NAME, state);
+ JsonNode value = null;
+ if (atObjectStart(jp)) {
+ value = jp.readValueAsTree();
+ jp.nextToken();
+ if (isEndOfRow(jp)) {
+ state.docFieldName = VALUE_FIELD_NAME;
+ Object doc = objectMapper.readValue(value, clazz);
+ endRow(jp, state);
+ return doc;
+ }
+ }
+ skipToField(jp, INCLUDED_DOC_FIELD_NAME, state);
+ if (atObjectStart(jp)) {
+ state.docFieldName = INCLUDED_DOC_FIELD_NAME;
+ Object doc = jp.readValueAs(clazz);
+ endRow(jp, state);
+ return doc;
+ }
+ return null;
+ }
+
+
+ private boolean isEndOfRow(JsonParser jp) {
+ return jp.getCurrentToken() == JsonToken.END_OBJECT;
+ }
+
+ private void endRow(JsonParser jp, ParseState state) throws IOException, JsonParseException {
+ state.inRow = false;
+ jp.nextToken();
+ }
+
+ private boolean atObjectStart(JsonParser jp) {
+ return jp.getCurrentToken() == JsonToken.START_OBJECT;
+ }
+
+ private boolean atEndOfRows(JsonParser jp) {
+ return jp.getCurrentToken() != JsonToken.START_OBJECT;
+ }
+ private void skipToField(JsonParser jp, String fieldName, ParseState state) throws JsonParseException, IOException {
+ String lastFieldName = null;
+ while (jp.getCurrentToken() != null) {
+ switch (jp.getCurrentToken()) {
+ case FIELD_NAME:
+ lastFieldName = jp.getCurrentName();
+ jp.nextToken();
+ break;
+ case START_OBJECT:
+ if (!state.inRow) {
+ state.inRow = true;
+ jp.nextToken();
+ } else {
+ if (isInField(fieldName, lastFieldName)) {
+ return;
+ } else {
+ jp.skipChildren();
+ }
+ }
+ break;
+ default:
+ if (isInField(fieldName, lastFieldName)) {
+ jp.nextToken();
+ return;
+ }
+ jp.nextToken();
+ break;
+ }
+ }
+ }
+
+ private boolean isInField(String fieldName, String lastFieldName) {
+ return lastFieldName != null && lastFieldName.equals(fieldName);
+ }
+
+
+ private Map readHeaderFields(JsonParser jp)
+ throws JsonParseException, IOException {
+ Map map = new HashMap();
+ jp.nextToken();
+ String nextFieldName = jp.getCurrentName();
+ while (!nextFieldName.equals(ROWS_FIELD_NAME)) {
+ jp.nextToken();
+ map.put(nextFieldName, Integer.valueOf(jp.getIntValue()));
+ jp.nextToken();
+ nextFieldName = jp.getCurrentName();
+ }
+ return map;
+ }
+
+ @Override
+ protected void writeInternal(Object o, HttpOutputMessage outputMessage)
+ throws IOException, HttpMessageNotWritableException {
+
+ JsonEncoding encoding = getEncoding(outputMessage.getHeaders()
+ .getContentType());
+ JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory()
+ .createJsonGenerator(outputMessage.getBody(), encoding);
+ try {
+ if (this.prefixJson) {
+ jsonGenerator.writeRaw("{} && ");
+ }
+ this.objectMapper.writeValue(jsonGenerator, o);
+ } catch (JsonGenerationException ex) {
+ throw new HttpMessageNotWritableException("Could not write JSON: "
+ + ex.getMessage(), ex);
+ }
+ }
+
+ private JsonEncoding getEncoding(MediaType contentType) {
+ if (contentType != null && contentType.getCharSet() != null) {
+ Charset charset = contentType.getCharSet();
+ for (JsonEncoding encoding : JsonEncoding.values()) {
+ if (charset.name().equals(encoding.getJavaName())) {
+ return encoding;
+ }
+ }
+ }
+ return JsonEncoding.UTF8;
+ }
+
+ private static class ParseState {
+ boolean inRow;
+ String docFieldName = "";
+ }
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
new file mode 100644
index 000000000..fc9d66c90
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-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.couchdb.monitor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Base class to encapsulate common configuration settings when connecting to a CouchDB database
+ *
+ * @author Mark Pollack
+ *
+ */
+public abstract class AbstractMonitor {
+
+
+ protected RestTemplate restTemplate;
+ protected String databaseUrl;
+
+ /**
+ * Gets the databaseUrl used to connect to CouchDB
+ * @return
+ */
+ public String getDatabaseUrl() {
+ return this.databaseUrl;
+ }
+
+
+
+
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
new file mode 100644
index 000000000..80159a669
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-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.couchdb.monitor;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Map;
+
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Expose basic server information via JMX
+ *
+ * @author Mark Pollack
+ *
+ */
+@ManagedResource(description="Server Information")
+public class ServerInfo extends AbstractMonitor {
+
+
+ public ServerInfo(String databaseUrl) {
+ this.databaseUrl = databaseUrl;
+ this.restTemplate = new RestTemplate();
+ }
+
+
+ @ManagedOperation(description="Server host name")
+ public String getHostName() throws UnknownHostException {
+ return InetAddress.getLocalHost().getHostName();
+ }
+
+
+ @ManagedOperation(description="CouchDB Server Version")
+ public String getVersion() {
+ return (String) getRoot().get("version");
+ }
+
+ @ManagedOperation(description="Message of the day")
+ public String getMotd() {
+ return (String) getRoot().get("greeting");
+ }
+
+ public Map getRoot() {
+ Map map = restTemplate.getForObject(getDatabaseUrl(),Map.class);
+ return map;
+ }
+
+}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/package-info.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/package-info.java
new file mode 100644
index 000000000..574efceb3
--- /dev/null
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * CouchDB specific JMX monitoring support.
+ */
+package org.springframework.data.document.couchdb.monitor;
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbUtils.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
similarity index 61%
rename from spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbUtils.java
rename to spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
index 65c7084d3..823454ca9 100644
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/CouchDbUtils.java
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package org.springframework.data.document.couchdb;
+package org.springframework.data.document.couchdb.support;
-import org.jcouchdb.exception.CouchDBException;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.UncategorizedDocumentStoreException;
/**
- * Helper class featuring helper methods for internal MongoDb classes.
+ * Helper class featuring helper methods for internal CouchDB classes.
*
* Mainly intended for internal use within the framework.
*
* @author Thomas Risberg
+ * @author Tareq Abedrabbo
* @since 1.0
*/
-public class CouchDbUtils {
+public abstract class CouchUtils {
/**
* Convert the given runtime exception to an appropriate exception from the
@@ -41,17 +41,38 @@ public class CouchDbUtils {
*/
public static DataAccessException translateCouchExceptionIfPossible(RuntimeException ex) {
- // Check for well-known MongoException subclasses.
-
- // All other MongoExceptions
- if (ex instanceof CouchDBException) {
- return new UncategorizedDocumentStoreException(ex.getMessage(), ex);
- }
-
- // If we get here, we have an exception that resulted from user code,
- // rather than the persistence provider, so we return null to indicate
- // that translation should not occur.
return null;
}
+
+ /**
+ * Adds an id variable to a URL
+ * @param url the URL to modify
+ * @return the modified URL
+ */
+ public static String addId(String url) {
+ return ensureTrailingSlash(url) + "{id}";
+ }
+
+
+ /**
+ * Adds a 'changes since' variable to a URL
+ * @param url
+ * @return
+ */
+ public static String addChangesSince(String url) {
+ return ensureTrailingSlash(url) + "_changes?since={seq}";
+ }
+
+ /**
+ * Ensures that a URL ends with a slash.
+ * @param url the URL to modify
+ * @return the modified URL
+ */
+ public static String ensureTrailingSlash(String url) {
+ if (!url.endsWith("/")) {
+ url += "/";
+ }
+ return url;
+ }
}
diff --git a/spring-data-couchdb/src/main/resources/META-INF/spring.handlers b/spring-data-couchdb/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 000000000..e8486b631
--- /dev/null
+++ b/spring-data-couchdb/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://www.springframework.org/schema/data/couch=org.springframework.data.document.couchdb.config.CouchNamespaceHandler
diff --git a/spring-data-couchdb/src/main/resources/META-INF/spring.schemas b/spring-data-couchdb/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 000000000..7ca5ca1c2
--- /dev/null
+++ b/spring-data-couchdb/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,2 @@
+http\://www.springframework.org/schema/data/couch/spring-couch-1.0.xsd=org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
+http\://www.springframework.org/schema/data/couch/spring-couch.xsd=org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
diff --git a/spring-data-couchdb/src/main/resources/META-INF/spring.tooling b/spring-data-couchdb/src/main/resources/META-INF/spring.tooling
new file mode 100644
index 000000000..5859f57de
--- /dev/null
+++ b/spring-data-couchdb/src/main/resources/META-INF/spring.tooling
@@ -0,0 +1,4 @@
+# Tooling related information for the Couch DB namespace
+http\://www.springframework.org/schema/data/couch@name=Couch Namespace
+http\://www.springframework.org/schema/data/couch@prefix=couch
+http\://www.springframework.org/schema/data/couch@icon=org/springframework/jdbc/config/spring-jdbc.gif
diff --git a/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd b/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
new file mode 100644
index 000000000..d4e21345b
--- /dev/null
+++ b/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
new file mode 100644
index 000000000..d43677696
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
@@ -0,0 +1,77 @@
+/*
+ * 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.couchdb;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+
+import java.util.Date;
+
+/**
+ * @author Tareq Abedrabbo (tareq.abedrabbo@opencredo.com)
+ * @since 13/01/2011
+ */
+@JsonIgnoreProperties(ignoreUnknown=true)
+public class DummyDocument {
+
+ private String message;
+
+ private String timestamp = new Date().toString();
+
+ public DummyDocument() {
+ }
+
+ public DummyDocument(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DummyDocument document = (DummyDocument) o;
+
+ if (message != null ? !message.equals(document.message) : document.message != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return message != null ? message.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "DummyDocument{" +
+ "message='" + message + '\'' +
+ ", timestamp=" + timestamp +
+ '}';
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
new file mode 100644
index 000000000..8f690bcc2
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
@@ -0,0 +1,51 @@
+/*
+ * 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.couchdb;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.springframework.http.HttpEntity;
+
+/**
+ * Matches the content of the body of an HttpEntity.
+ * @author Tareq Abedrabbo
+ * @since 31/01/2011
+ */
+public class IsBodyEqual extends TypeSafeMatcher {
+
+ private Object object;
+
+ public IsBodyEqual(Object object) {
+ this.object = object;
+ }
+
+ @Override
+ public boolean matchesSafely(HttpEntity httpEntity) {
+ return httpEntity.getBody().equals(object);
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("body equals ").appendValue(object);
+ }
+
+ @Factory
+ public static Matcher bodyEqual(Object object) {
+ return new IsBodyEqual(object);
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
new file mode 100644
index 000000000..6054a3742
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
@@ -0,0 +1,37 @@
+/*
+ * 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.couchdb.admin;
+
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.springframework.data.document.couchdb.core.CouchConstants;
+
+public class CouchAdminIntegrationTests {
+
+ @Test
+ public void dbLifecycle() {
+
+ CouchAdmin admin = new CouchAdmin(CouchConstants.COUCHDB_URL);
+ admin.deleteDatabase("foo");
+ List dbs = admin.listDatabases();
+ admin.createDatabase("foo");
+ List newDbs = admin.listDatabases();
+ Assert.assertEquals(dbs.size()+1, newDbs.size());
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
new file mode 100644
index 000000000..98fa48750
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
@@ -0,0 +1,124 @@
+/*
+ * 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.couchdb.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+import static org.springframework.http.HttpStatus.OK;
+
+/**
+ * Base class for CouchDB integration tests. Checks whether CouchDB is available before running each test,
+ * in which case the test is executed. If CouchDB is not available, tests are ignored.
+ *
+ * @author Tareq Abedrabbo (tareq.abedrabbo@opencredo.com)
+ * @since 13/01/2011
+ */
+
+public abstract class AbstractCouchTemplateIntegrationTests {
+
+
+ protected static final Log log = LogFactory.getLog(AbstractCouchTemplateIntegrationTests.class);
+
+ protected static final RestTemplate restTemplate = new RestTemplate();
+
+ /**
+ * This methods ensures that the database is running. Otherwise, the test is ignored.
+ */
+ @BeforeClass
+ public static void assumeDatabaseIsUpAndRunning() {
+ try {
+ ResponseEntity responseEntity = restTemplate.getForEntity(CouchConstants.COUCHDB_URL, String.class);
+ assumeTrue(responseEntity.getStatusCode().equals(OK));
+ log.debug("CouchDB is running on " + CouchConstants.COUCHDB_URL +
+ " with status " + responseEntity.getStatusCode());
+ } catch (RestClientException e) {
+ log.debug("CouchDB is not running on " + CouchConstants.COUCHDB_URL);
+ assumeNoException(e);
+ }
+ }
+
+ @Before
+ public void setUpTestDatabase() throws Exception {
+ RestTemplate template = new RestTemplate();
+ template.setErrorHandler(new DefaultResponseErrorHandler(){
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ // do nothing, error status will be handled in the switch statement
+ }
+ });
+ ResponseEntity response = template.getForEntity(CouchConstants.TEST_DATABASE_URL, String.class);
+ HttpStatus statusCode = response.getStatusCode();
+ switch (statusCode) {
+ case NOT_FOUND:
+ createNewTestDatabase();
+ break;
+ case OK:
+ deleteExisitingTestDatabase();
+ createNewTestDatabase();
+ break;
+ default:
+ throw new IllegalStateException("Unsupported http status [" + statusCode + "]");
+ }
+ }
+
+ private void deleteExisitingTestDatabase() {
+ restTemplate.delete(CouchConstants.TEST_DATABASE_URL);
+ }
+
+ private void createNewTestDatabase() {
+ restTemplate.put(CouchConstants.TEST_DATABASE_URL, null);
+ }
+
+ /**
+ * Reads a CouchDB document and converts it to the expected type.
+ */
+ protected T getDocument(String id, Class expectedType) {
+ String url = CouchConstants.TEST_DATABASE_URL + "{id}";
+ return restTemplate.getForObject(url, expectedType, id);
+ }
+
+ /**
+ * Writes a CouchDB document
+ */
+ protected String putDocument(Object document) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ HttpEntity request = new HttpEntity(document, headers);
+ String id = UUID.randomUUID().toString();
+ restTemplate.put(CouchConstants.TEST_DATABASE_URL + "{id}", request, id);
+ return id;
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
new file mode 100644
index 000000000..14412b947
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
@@ -0,0 +1,27 @@
+/*
+ * 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.couchdb.core;
+
+public abstract class CouchConstants {
+
+ public static final String COUCHDB_URL = "http://127.0.0.1:5984/";
+ public static final String TEST_DATABASE_URL = COUCHDB_URL + "si_couchdb_test/";
+
+ public CouchConstants() {
+ // TODO Auto-generated constructor stub
+ }
+
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
new file mode 100644
index 000000000..33411243b
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
@@ -0,0 +1,41 @@
+/*
+ * 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.couchdb.core;
+
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.data.document.couchdb.DummyDocument;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.util.Assert.*;
+
+public class CouchTemplateIntegrationTests extends AbstractCouchTemplateIntegrationTests {
+
+
+ @Test
+ public void saveAndFindTest() {
+ CouchTemplate template = new CouchTemplate(CouchConstants.TEST_DATABASE_URL);
+ DummyDocument document = new DummyDocument("hello");
+ String id = UUID.randomUUID().toString();
+ template.save(id, document);
+ DummyDocument foundDocument = template.findOne(id, DummyDocument.class);
+ Assert.assertEquals(document.getMessage(), foundDocument.getMessage());
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
new file mode 100644
index 000000000..b0163aa43
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
@@ -0,0 +1,32 @@
+/*
+ * 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.couchdb.core;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for CouchTemplate with mocks
+ *
+ *
+ */
+public class CouchTemplateTests {
+
+ @Test
+ public void foo() {
+
+ }
+}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
new file mode 100644
index 000000000..07cf47222
--- /dev/null
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2010 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.couchdb.monitor;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Server application to test JMX functionality.
+ *
+ * @author Mark Pollack
+ */
+public class JmxServer {
+
+ public static void main(String[] args) {
+ new JmxServer().run();
+ }
+
+ public void run() {
+ new ClassPathXmlApplicationContext(new String[] {"server-jmx.xml"} );
+ }
+
+}
diff --git a/spring-data-couchdb/src/test/resources/log4j.properties b/spring-data-couchdb/src/test/resources/log4j.properties
new file mode 100644
index 000000000..6d5422d74
--- /dev/null
+++ b/spring-data-couchdb/src/test/resources/log4j.properties
@@ -0,0 +1,13 @@
+log4j.rootCategory=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
+
+log4j.category.org.apache.activemq=ERROR
+log4j.category.org.springframework.batch=DEBUG
+log4j.category.org.springframework.transaction=INFO
+
+log4j.category.org.hibernate.SQL=DEBUG
+# for debugging datasource initialization
+# log4j.category.test.jdbc=DEBUG
diff --git a/spring-data-couchdb/src/test/resources/server-jmx.xml b/spring-data-couchdb/src/test/resources/server-jmx.xml
new file mode 100644
index 000000000..30c1933fe
--- /dev/null
+++ b/spring-data-couchdb/src/test/resources/server-jmx.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-data-couchdb/template.mf b/spring-data-couchdb/template.mf
index a03fa6ca7..af4bc48ff 100644
--- a/spring-data-couchdb/template.mf
+++ b/spring-data-couchdb/template.mf
@@ -8,10 +8,15 @@ Import-Template:
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.dao.*;version="[3.0.0, 4.0.0)",
+ org.springframework.http.*;version="[3.0.0, 4.0.0)",
+ org.springframework.web.*;version="[3.0.0, 4.0.0)",
org.springframework.util.*;version="[3.0.0, 4.0.0)",
+ org.springframework.context.*;version="[3.0.0, 4.0.0)",
+ org.springframework.jmx.*;version="[3.0.0, 4.0.0)",
+ org.springframework.remoting.*;version="[3.0.0, 4.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.springframework.data.document.*;version="[1.0.0, 2.0.0)",
- org.jcouchdb.*;version="0",
+ org.codehaus.jackson.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0"
diff --git a/spring-data-document-parent/pom.xml b/spring-data-document-parent/pom.xml
index 077bf9455..e3d4eead3 100644
--- a/spring-data-document-parent/pom.xml
+++ b/spring-data-document-parent/pom.xml
@@ -15,6 +15,7 @@
1.2.15
1.8.4
1.5.10
+ 1.6.1
3.0.5.RELEASE
1.0.0.BUILD-SNAPSHOT
1.6.11.M2
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/analytics/MvcAnalyticsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/analytics/MvcAnalyticsTests.java
index f69d2bc35..b2863a503 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/analytics/MvcAnalyticsTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/analytics/MvcAnalyticsTests.java
@@ -47,6 +47,8 @@ public class MvcAnalyticsTests {
@Test
public void loadMvcEventData() {
+ mongoTemplate.dropCollection("mvc");
+ mongoTemplate.createCollection("mvc");
// datasize, favoriteRestId
createAndStoreMvcEvent(5, 1);
createAndStoreMvcEvent(6, 2);
From d6fd36ce003dd2050ee90a463ae36e1a2fd108c2 Mon Sep 17 00:00:00 2001
From: Mark Pollack
Date: Mon, 7 Mar 2011 14:56:42 -0500
Subject: [PATCH 9/9] Igore CouchDB integration tests until couchdb server is
on CI build machine
---
.../document/couchdb/admin/CouchAdminIntegrationTests.java | 2 ++
.../document/couchdb/core/CouchTemplateIntegrationTests.java | 5 ++---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
index 6054a3742..93c13de54 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
@@ -19,12 +19,14 @@ import java.util.List;
import junit.framework.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.document.couchdb.core.CouchConstants;
public class CouchAdminIntegrationTests {
@Test
+ @Ignore("until CI has couch server running")
public void dbLifecycle() {
CouchAdmin admin = new CouchAdmin(CouchConstants.COUCHDB_URL);
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
index 33411243b..893f32471 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
@@ -20,16 +20,15 @@ import java.util.UUID;
import junit.framework.Assert;
+import org.junit.Ignore;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.springframework.data.document.couchdb.DummyDocument;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.util.Assert.*;
public class CouchTemplateIntegrationTests extends AbstractCouchTemplateIntegrationTests {
@Test
+ @Ignore("until CI has couch server running")
public void saveAndFindTest() {
CouchTemplate template = new CouchTemplate(CouchConstants.TEST_DATABASE_URL);
DummyDocument document = new DummyDocument("hello");