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) obj, mapDbObj); dbo.put(name, mapDbObj); - } else { - if (null != dbref) { - DBRef dbRef = createDBRef(obj, dbref); - dbo.put(name, dbRef); - } else { - BasicDBObject propDbObj = new BasicDBObject(); - write(obj, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation())); - dbo.put(name, propDbObj); + return; + } + + if (null != dbref) { + DBRef dbRefObj = createDBRef(obj, dbref); + if (null != dbRefObj) { + dbo.put(name, dbRefObj); + return; } } + + // Lookup potential custom target type + Class basicTargetType = customTypeMapping.get(obj.getClass()); + + if (basicTargetType != null) { + dbo.put(name, conversionService.convert(obj, basicTargetType)); + return; + } + + BasicDBObject propDbObj = new BasicDBObject(); + write(obj, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation())); + dbo.put(name, propDbObj); + } protected void writeMapInternal(Map obj, DBObject dbo) { @@ -510,7 +554,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext continue; } if (null != entry.getValue() && entry.getValue() instanceof DBObject) { - m.put(entry.getKey(), read((null != toType ? toType : prop.getTypeInformation().getMapValueType()), (DBObject) entry.getValue())); + m.put(entry.getKey(), read((null != toType ? toType : prop.getMapValueType()), (DBObject) entry.getValue())); } else { m.put(entry.getKey(), entry.getValue()); } @@ -518,7 +562,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext return m; } else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) { // It's empty - return Array.newInstance(prop.getType().getComponentType(), 0); + return Array.newInstance(prop.getComponentType(), 0); } else if (prop.isCollection() && dbObj instanceof BasicDBList) { BasicDBList dbObjList = (BasicDBList) dbObj; Object[] items = (Object[]) Array.newInstance(prop.getComponentType(), dbObjList.size()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java index 404226d45..11661eed6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java @@ -32,12 +32,9 @@ import org.bson.types.ObjectId; import org.springframework.beans.BeanUtils; import org.springframework.core.CollectionFactory; import org.springframework.core.convert.ConversionFailedException; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.ConditionalGenericConverter; +import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor; @@ -54,6 +51,7 @@ import org.springframework.util.comparator.CompoundComparator; public class SimpleMongoConverter implements MongoConverter { private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class); + private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class); private static final Set SIMPLE_TYPES; static { @@ -112,8 +110,8 @@ public class SimpleMongoConverter implements MongoConverter { * Creates a {@link SimpleMongoConverter}. */ public SimpleMongoConverter() { - this.conversionService = new SimpleToStringSuppressingGenericConversionService(); - ConversionServiceFactory.addDefaultConverters(conversionService); + this.conversionService = ConversionServiceFactory.createDefaultConversionService(); + this.conversionService.removeConvertible(Object.class, String.class); initializeConverters(); } @@ -123,12 +121,10 @@ public class SimpleMongoConverter implements MongoConverter { */ protected void initializeConverters() { - conversionService.addConverter(ObjectIdToStringConverter.INSTANCE); conversionService.addConverter(StringToObjectIdConverter.INSTANCE); conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE); conversionService.addConverter(BigIntegerToIdConverter.INSTANCE); - } /** @@ -237,15 +233,32 @@ public class SimpleMongoConverter implements MongoConverter { return; } - if (conversionService.canConvert(value.getClass(), String.class)) { - dbo.put(keyToUse, conversionService.convert(value, String.class)); + Class customTargetType = getCustomTargetType(value); + if (customTargetType != null) { + dbo.put(keyToUse, conversionService.convert(value, customTargetType)); return; } DBObject nestedDbo = new BasicDBObject(); write(value, nestedDbo); dbo.put(keyToUse, nestedDbo); - + } + + /** + * Returns whether the {@link ConversionService} has a custom {@link Converter} registered that can convert the given + * object into one of the types supported by MongoDB. + * + * @param obj + * @return + */ + private Class getCustomTargetType(Object obj) { + + for (Class mongoType : MONGO_TYPES) { + if (conversionService.canConvert(obj.getClass(), mongoType)) { + return mongoType; + } + } + return null; } /** @@ -510,30 +523,6 @@ public class SimpleMongoConverter implements MongoConverter { public ObjectId convertObjectId(Object id) { return conversionService.convert(id, ObjectId.class); } - - private static class SimpleToStringSuppressingGenericConversionService extends GenericConversionService { - - private static final Set REFERENCE = Collections.singleton(new ConvertiblePair(Object.class, String.class)); - - /* (non-Javadoc) - * @see org.springframework.core.convert.support.GenericConversionService#getConverter(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) - */ - @Override - protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { - - GenericConverter converter = super.getConverter(sourceType, targetType); - - if (converter instanceof ConditionalGenericConverter) { - - Set convertibleTypes = ((ConditionalGenericConverter) converter).getConvertibleTypes(); - if (REFERENCE.equals(convertibleTypes)) { - return null; - } - } - - return converter; - } - } /** * Simple singleton to convert {@link ObjectId}s to their {@link String} representation. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/CollectionCreatedEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/CollectionCreatedEvent.java new file mode 100644 index 000000000..a07356fc8 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/CollectionCreatedEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.event; + +import com.mongodb.DBObject; +import org.springframework.context.ApplicationEvent; + +/** + * @author Jon Brisbin + */ +public class CollectionCreatedEvent extends ApplicationEvent { + + private final DBObject options; + + public CollectionCreatedEvent(String collection, DBObject options) { + super(collection); + this.options = options; + } + + public String getCollection() { + return (String) super.getSource(); + } + + public DBObject getOptions() { + return options; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/EventType.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/EventType.java new file mode 100644 index 000000000..3cb5336ce --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/EventType.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.event; + +/** + * @author Jon Brisbin + */ +public enum EventType { + COLLECTION_CREATED, + DOCUMENT_INSERTED, + DOCUMENT_UPDATED, + DOCUMENT_DELETED +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/InsertEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/InsertEvent.java new file mode 100644 index 000000000..94121ff05 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/InsertEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.event; + +import com.mongodb.DBObject; +import org.springframework.context.ApplicationEvent; + +/** + * @author Jon Brisbin + */ +public class InsertEvent extends ApplicationEvent { + + private final String collection; + + public InsertEvent(String collection, DBObject dbo) { + super(dbo); + this.collection = collection; + } + + public String getCollection() { + return collection; + } + + public DBObject getDBObject() { + return (DBObject) source; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/SaveEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/SaveEvent.java new file mode 100644 index 000000000..c9e40b765 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/SaveEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.event; + +import com.mongodb.DBObject; +import org.springframework.context.ApplicationEvent; + +/** + * @author Jon Brisbin + */ +public class SaveEvent extends ApplicationEvent { + + private final String collection; + + public SaveEvent(String collection, DBObject source) { + super(source); + this.collection = collection; + } + + public DBObject getDBObject() { + return (DBObject) source; + } + + public String getCollection() { + return collection; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/WriteResultEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/WriteResultEvent.java new file mode 100644 index 000000000..bfbedc9c7 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/event/WriteResultEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.event; + +import com.mongodb.WriteResult; +import org.springframework.context.ApplicationEvent; + +/** + * @author Jon Brisbin + */ +public class WriteResultEvent extends ApplicationEvent { + + public WriteResultEvent(WriteResult result) { + super(result); + } + + public WriteResult getWriteResult() { + return (WriteResult) source; + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/event/MongoMappingEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/event/MongoMappingEvent.java new file mode 100644 index 000000000..f422a651c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/event/MongoMappingEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.mapping.event; + +import org.springframework.context.ApplicationEvent; +import org.springframework.data.document.mongodb.event.EventType; +import org.springframework.data.mapping.model.PersistentEntity; + +/** + * @author Jon Brisbin + */ +public class MongoMappingEvent extends ApplicationEvent { + + private final EventType type; + private final PersistentEntity entity; + + public MongoMappingEvent(EventType type, PersistentEntity entity, T target) { + super(target); + this.type = type; + this.entity = entity; + } + + public EventType getType() { + return type; + } + + public PersistentEntity getEntity() { + return entity; + } + + @Override + public T getSource() { + return (T) super.getSource(); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java index 892f1b3e5..4ca57c3ae 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java @@ -21,15 +21,22 @@ import static org.junit.Assert.*; import java.lang.reflect.Field; import java.math.BigInteger; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.util.JSON; + +import org.hamcrest.CoreMatchers; +import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; +import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum; import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum; import org.springframework.data.document.mongodb.convert.SimpleMongoConverter; @@ -292,10 +299,50 @@ public class SimpleMongoConverterTests { (DBObject) JSON.parse("{\"_id\" : {\"$oid\" : \"4d24809660413b687f5d323e\" }}")); assertThat(result.getId().toString(16), is("4d24809660413b687f5d323e")); } + + @Test + public void convertsAddressCorrectly() { + + Address address = new Address(); + address.city = "New York"; + address.street = "Broadway"; + + DBObject dbObject = new BasicDBObject(); + + converter.write(address, dbObject); + + assertThat(dbObject.get("city").toString(), is("New York")); + assertThat(dbObject.get("street").toString(), is("Broadway")); + + Address result = converter.read(Address.class, dbObject); + assertThat(result.city, is("New York")); + assertThat(result.street, is("Broadway")); + } + + @Test + public void convertsJodaTimeTypesCorrectly() { + + Set> converters = new HashSet>(); + converters.add(new LocalDateToDateConverter()); + converters.add(new DateToLocalDateConverter()); + + converter.setConverters(converters); + + AnotherPerson person = new AnotherPerson(); + person.birthDate = new LocalDate(); + + DBObject dbObject = new BasicDBObject(); + converter.write(person, dbObject); + + assertTrue(dbObject.get("birthDate") instanceof Date); + + AnotherPerson result = converter.read(AnotherPerson.class, dbObject); + assertThat(result.getBirthDate(), is(notNullValue())); + } private void assertListOfStringAndLong(List> types) { - assertThat(types.size(), is(2)); + assertThat(types.size(), CoreMatchers.is(2)); assertEquals(String.class, types.get(0)); assertEquals(Long.class, types.get(1)); } @@ -347,4 +394,39 @@ public class SimpleMongoConverterTests { return id; } } + + public static class Address { + String street; + String city; + + public String getStreet() { + return street; + } + + public String getCity() { + return city; + } + } + + public static class AnotherPerson { + LocalDate birthDate; + + public LocalDate getBirthDate() { + return birthDate; + } + } + + private class LocalDateToDateConverter implements Converter { + + public Date convert(LocalDate source) { + return source.toDateMidnight().toDate(); + } + } + + private class DateToLocalDateConverter implements Converter { + + public LocalDate convert(Date source) { + return new LocalDate(source.getTime()); + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java index d8dca29ab..539aabcac 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java @@ -25,24 +25,32 @@ import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter; import org.springframework.data.mapping.BasicMappingContext; -import org.springframework.data.mapping.model.MappingContext; /** * Unit tests for testing the mapping works with generic types. * * @author Oliver Gierke */ +@RunWith(MockitoJUnitRunner.class) public class GenericMappingTests { - MappingContext context; + BasicMappingContext context; MongoConverter converter; + + @Mock + ApplicationContext applicationContext; @Before public void setUp() { context = new BasicMappingContext(new MongoMappingConfigurationBuilder(null)); + context.setApplicationContext(applicationContext); context.addPersistentEntity(StringWrapper.class); converter = new MappingMongoConverter(context); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/InsertEventListener.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/InsertEventListener.java new file mode 100644 index 000000000..0e7f4a3ce --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/InsertEventListener.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.mapping; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.data.document.mongodb.event.InsertEvent; + +/** + * @author Jon Brisbin + */ +public class InsertEventListener implements ApplicationListener { + + private Logger log = LoggerFactory.getLogger(getClass()); + private AtomicInteger counter = new AtomicInteger(0); + + public void onApplicationEvent(InsertEvent event) { + log.info("Got INSERT event: " + event); + counter.incrementAndGet(); + } + + public int getCount() { + return counter.get(); + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java new file mode 100644 index 000000000..29f534393 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.mapping; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.joda.time.LocalDate; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.document.mongodb.convert.MappingMongoConverter; +import org.springframework.data.mapping.BasicMappingContext; +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +/** + * Unit tests for {@link MappingMongoConverter}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class MappingMongoConverterUnitTests { + + MappingMongoConverter converter; + BasicMappingContext mappingContext; + + @Mock + ApplicationContext applicationContext; + + @Before + public void setUp() { + mappingContext = new BasicMappingContext(); + mappingContext.setApplicationContext(applicationContext); + converter = new MappingMongoConverter(mappingContext); + } + + @Test + public void convertsAddressCorrectly() { + + Address address = new Address(); + address.city = "New York"; + address.street = "Broadway"; + + DBObject dbObject = new BasicDBObject(); + + converter.write(address, dbObject); + + assertThat(dbObject.get("city").toString(), is("New York")); + assertThat(dbObject.get("street").toString(), is("Broadway")); + } + + @Test + public void convertsJodaTimeTypesCorrectly() { + + List> converters = new ArrayList>(); + converters.add(new LocalDateToDateConverter()); + converters.add(new DateToLocalDateConverter()); + + List> customSimpleTypes = new ArrayList>(); + customSimpleTypes.add(LocalDate.class); + mappingContext.setCustomSimpleTypes(customSimpleTypes); + + converter = new MappingMongoConverter(mappingContext, converters); + + Person person = new Person(); + person.birthDate = new LocalDate(); + + DBObject dbObject = new BasicDBObject(); + converter.write(person, dbObject); + + assertTrue(dbObject.get("birthDate") instanceof Date); + + Person result = converter.read(Person.class, dbObject); + assertThat(result.birthDate, is(notNullValue())); + } + + + public static class Address { + String street; + String city; + } + + public static class Person { + LocalDate birthDate; + } + + private class LocalDateToDateConverter implements Converter { + + public Date convert(LocalDate source) { + return source.toDateMidnight().toDate(); + } + } + + private class DateToLocalDateConverter implements Converter { + + public LocalDate convert(Date source) { + return new LocalDate(source.getTime()); + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java index d87d043e5..b342afa90 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java @@ -16,8 +16,7 @@ package org.springframework.data.document.mongodb.mapping; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; @@ -30,6 +29,7 @@ import com.mongodb.DBObject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.data.document.mongodb.MongoTemplate; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.query.Criteria; @@ -45,12 +45,16 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration("classpath:mapping.xml") public class MappingTests { + @Autowired + ApplicationContext applicationContext; @Autowired MongoTemplate template; @Autowired BasicMappingContext mappingContext; @Autowired MappingMongoConverter mongoConverter; + @Autowired + InsertEventListener insertEventListener; @Test public void setUp() { @@ -144,6 +148,11 @@ public class MappingTests { assertNotNull(p.getId()); } + @Test + public void testEventHandling() { + assertThat(insertEventListener.getCount(), greaterThan(0)); + } + @Test public void testReadEntity() { List result = template.find(new Query(Criteria.where("ssn").is(123456789)), Person.class); @@ -151,4 +160,5 @@ public class MappingTests { assertThat(result.get(0).getAddress().getCountry(), is("USA")); assertThat(result.get(0).getAccounts(), notNullValue()); } + } diff --git a/spring-data-mongodb/src/test/resources/mapping.xml b/spring-data-mongodb/src/test/resources/mapping.xml index 010431b54..65ee2cfed 100644 --- a/spring-data-mongodb/src/test/resources/mapping.xml +++ b/spring-data-mongodb/src/test/resources/mapping.xml @@ -30,4 +30,5 @@ +