diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java index 87e143059..0a5036cfa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java @@ -18,6 +18,9 @@ package org.springframework.data.mongodb.config; import static org.springframework.data.mongodb.config.BeanNames.*; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,13 +39,20 @@ import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; @@ -133,15 +143,29 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser { List customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters"); if (customConvertersElements.size() == 1) { + Element customerConvertersElement = customConvertersElements.get(0); ManagedList converterBeans = new ManagedList(); List converterElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter"); + if (converterElements != null) { for (Element listenerElement : converterElements) { converterBeans.add(parseConverter(listenerElement, parserContext)); } } + // Scan for Converter and GenericConverter beans in the given base-package + String packageToScan = customerConvertersElement.getAttribute(BASE_PACKAGE); + if (StringUtils.hasText(packageToScan)) { + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true); + provider.addExcludeFilter(new NegatingFilter(new AssignableTypeFilter(Converter.class), new AssignableTypeFilter( + GenericConverter.class))); + + for (BeanDefinition candidate : provider.findCandidateComponents(packageToScan)) { + converterBeans.add(candidate); + } + } + BeanDefinitionBuilder conversionsBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomConversions.class); conversionsBuilder.addConstructorArgValue(converterBeans); @@ -194,4 +218,39 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser { "Element must specify 'ref' or contain a bean definition for the converter", element); return null; } + + /** + * {@link TypeFilter} that returns {@literal false} in case any of the given delegates matches. + * + * @author Oliver Gierke + */ + private static class NegatingFilter implements TypeFilter { + + private final Set delegates; + + /** + * Creates a new {@link NegatingFilter} with the given delegates. + * + * @param filters + */ + public NegatingFilter(TypeFilter... filters) { + Assert.notNull(filters); + this.delegates = new HashSet(Arrays.asList(filters)); + } + + /* + * (non-Javadoc) + * @see org.springframework.core.type.filter.TypeFilter#match(org.springframework.core.type.classreading.MetadataReader, org.springframework.core.type.classreading.MetadataReaderFactory) + */ + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { + + for (TypeFilter delegate : delegates) { + if (delegate.match(metadataReader, metadataReaderFactory)) { + return false; + } + } + + return true; + } + } } diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd index b8d4d8d72..fd1dd8a0d 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd @@ -11,7 +11,6 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - - @@ -152,9 +148,10 @@ The Mongo URI string.]]> - + + + + @@ -162,14 +159,14 @@ Defines a MongoConverter for getting rich mapping functionality. + domain objects to and from Mongo's DBObject]]> + diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java index c7d30ea7b..fba2daf01 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java @@ -15,10 +15,26 @@ */ package org.springframework.data.mongodb.config; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Collections; +import java.util.Set; + +import org.junit.Before; import org.junit.Test; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.io.ClassPathResource; +import org.springframework.data.mongodb.core.convert.CustomConversions; +import org.springframework.data.mongodb.core.mapping.Account; +import org.springframework.data.mongodb.repository.Person; +import org.springframework.stereotype.Component; + +import com.mongodb.DBObject; /** * Integration tests for {@link MongoParser}. @@ -26,13 +42,48 @@ import org.springframework.core.io.ClassPathResource; * @author Oliver Gierke */ public class MappingMongoConverterParserIntegrationTests { + + DefaultListableBeanFactory factory; + + + @Before + public void setUp() { + factory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); + reader.loadBeanDefinitions(new ClassPathResource("namespace/converter.xml")); + } @Test public void allowsDbFactoryRefAttribute() { - - ConfigurableListableBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/converter.xml")); factory.getBeanDefinition("converter"); factory.getBean("converter"); } + + @Test + public void scansForConverterAndSetsUpCustomConversionsAccordingly() { + + CustomConversions conversions = factory.getBean(CustomConversions.class); + assertThat(conversions.hasCustomWriteTarget(Person.class), is(true)); + assertThat(conversions.hasCustomWriteTarget(Account.class), is(true)); + } + + @Component + public static class SampleConverter implements Converter { + public DBObject convert(Person source) { + return null; + } + } + + @Component + public static class SampleConverterFactory implements GenericConverter { + + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(Account.class, DBObject.class)); + } + + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return null; + } + } } diff --git a/spring-data-mongodb/src/test/resources/namespace/converter.xml b/spring-data-mongodb/src/test/resources/namespace/converter.xml index 8c1400041..45189bcbc 100644 --- a/spring-data-mongodb/src/test/resources/namespace/converter.xml +++ b/spring-data-mongodb/src/test/resources/namespace/converter.xml @@ -5,7 +5,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - + + +