diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 0d7fb7449..7d8e59dd5 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -152,6 +152,13 @@
junit
test
+
+
+ joda-time
+ joda-time
+ 1.6
+ test
+
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
index 43054b705..266f1ac23 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java
@@ -22,6 +22,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
@@ -37,9 +40,13 @@ import com.mongodb.util.JSON;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
+import org.springframework.beans.BeansException;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
@@ -47,6 +54,9 @@ import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoP
import org.springframework.data.document.mongodb.convert.MappingMongoConverter;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
+import org.springframework.data.document.mongodb.event.CollectionCreatedEvent;
+import org.springframework.data.document.mongodb.event.InsertEvent;
+import org.springframework.data.document.mongodb.event.SaveEvent;
import org.springframework.data.document.mongodb.query.IndexDefinition;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.document.mongodb.query.Update;
@@ -61,7 +71,7 @@ import org.springframework.util.Assert;
* @author Mark Pollack
* @author Oliver Gierke
*/
-public class MongoTemplate implements InitializingBean, MongoOperations {
+public class MongoTemplate implements InitializingBean, MongoOperations, ApplicationContextAware {
private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
@@ -87,7 +97,9 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
private String databaseName;
private String username;
private String password;
-
+ private ApplicationContext applicationContext;
+ private ExecutorService eventPublishers = Executors.newCachedThreadPool();
+ private LinkedBlockingQueue eventQueue = new LinkedBlockingQueue();
/**
* Constructor used for a basic template configuration
@@ -173,6 +185,9 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
}
}
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
/**
* Sets the username to use to connect to the Mongo database
@@ -548,6 +563,9 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
writer.write(objectToSave, dbDoc);
Object id = insertDBObject(collectionName, dbDoc);
populateIdIfNecessary(objectToSave, id);
+ if (null != applicationContext) {
+ eventQueue.add(new InsertEvent(collectionName, dbDoc));
+ }
}
/* (non-Javadoc)
@@ -621,6 +639,9 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
writer.write(objectToSave, dbDoc);
ObjectId id = saveDBObject(collectionName, dbDoc);
populateIdIfNecessary(objectToSave, id);
+ if (null != applicationContext) {
+ eventQueue.add(new SaveEvent(collectionName, dbDoc));
+ }
}
@@ -806,7 +827,11 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
protected DBCollection doCreateCollection(final String collectionName, final DBObject collectionOptions) {
return execute(new DbCallback() {
public DBCollection doInDB(DB db) throws MongoException, DataAccessException {
- return db.createCollection(collectionName, collectionOptions);
+ DBCollection coll = db.createCollection(collectionName, collectionOptions);
+ if (null != applicationContext) {
+ eventQueue.add(new CollectionCreatedEvent(collectionName, collectionOptions));
+ }
+ return coll;
}
});
}
@@ -1043,6 +1068,21 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
((MappingMongoConverter) mongoConverter).setMongo(mongo);
((MappingMongoConverter) mongoConverter).setDefaultDatabase(databaseName);
}
+ if (null != applicationContext) {
+ eventPublishers.submit(new Runnable() {
+ public void run() {
+ while (true) {
+ ApplicationEvent event = null;
+ try {
+ event = eventQueue.take();
+ applicationContext.publishEvent(event);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+ });
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
index 3de89b155..000531d95 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
@@ -22,6 +22,8 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -41,6 +43,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.core.GenericTypeResolver;
+import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
@@ -58,14 +62,21 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
+ * {@link MongoConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to
+ * {@link DBObject}.
+ *
* @author Jon Brisbin
+ * @author Oliver Gierke
*/
public class MappingMongoConverter implements MongoConverter, ApplicationContextAware {
private static final String CUSTOM_TYPE_KEY = "_class";
+ @SuppressWarnings({"unchecked"})
+ private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class);
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
- protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
+ protected final GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
+ protected final Map, Class>> customTypeMapping = new HashMap, Class>>();
protected SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
protected MappingContext mappingContext;
protected ApplicationContext applicationContext;
@@ -87,12 +98,26 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
this.mappingContext = mappingContext;
if (null != converters) {
for (Converter, ?> c : converters) {
+ registerConverter(c);
conversionService.addConverter(c);
}
}
initializeConverters();
}
+ /**
+ * Inspects the given {@link Converter} for the types it can convert and registers the pair for custom type conversion
+ * in case the target type is a Mongo basic type.
+ *
+ * @param converter
+ */
+ private void registerConverter(Converter, ?> converter) {
+ Class>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
+ if (MONGO_TYPES.contains(arguments[1])) {
+ customTypeMapping.put(arguments[0], arguments[1]);
+ }
+ }
+
public MappingContext getMappingContext() {
return mappingContext;
}
@@ -328,13 +353,6 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
entity.doWithProperties(new PropertyHandler() {
public void doWithPersistentProperty(PersistentProperty prop) {
String name = prop.getName();
- if (null != idProperty && name.equals(idProperty.getName())) {
- return;
- }
- if (prop.isAssociation()) {
- return;
- }
-
Class> type = prop.getType();
Object propertyObj = null;
try {
@@ -377,7 +395,14 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
this.applicationContext = applicationContext;
}
+ /**
+ * Registers converters for {@link ObjectId} handling, removes plain {@link #toString()} converter and promotes the
+ * configured {@link ConversionService} to {@link MappingBeanHelper}.
+ */
protected void initializeConverters() {
+
+ this.conversionService.removeConvertible(Object.class, String.class);
+
if (!conversionService.canConvert(ObjectId.class, String.class)) {
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
@@ -386,15 +411,18 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
}
+
+ MappingBeanHelper.setConversionService(conversionService);
}
@SuppressWarnings({"unchecked"})
protected void writePropertyInternal(PersistentProperty prop, Object obj, DBObject dbo) {
org.springframework.data.document.mongodb.mapping.DBRef dbref = prop.getField()
.getAnnotation(org.springframework.data.document.mongodb.mapping.DBRef.class);
+
String name = prop.getName();
+ Class> type = prop.getType();
if (prop.isCollection()) {
- Class> type = prop.getType();
BasicDBList dbList = new BasicDBList();
Collection> coll = (type.isArray() ? Arrays.asList((Object[]) obj) : (Collection>) obj);
for (Object propObjItem : coll) {
@@ -408,20 +436,36 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
dbo.put(name, dbList);
- } else if (null != obj && obj instanceof Map) {
+ return;
+ }
+
+ if (null != obj && obj instanceof Map) {
BasicDBObject mapDbObj = new BasicDBObject();
writeMapInternal((Map