DATADOC-119 added support for registering custom converters with the mongo namespace support

This commit is contained in:
Thomas Risberg
2011-05-11 15:00:46 -04:00
parent d7f33774e0
commit 9b86637031
7 changed files with 293 additions and 1 deletions

View File

@@ -16,15 +16,21 @@
package org.springframework.data.document.mongodb.config;
import java.util.List;
import java.util.Set;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -37,6 +43,7 @@ import org.springframework.data.document.mongodb.mapping.MongoMappingContext;
import org.springframework.data.document.mongodb.mapping.MongoPersistentEntityIndexCreator;
import org.springframework.data.mapping.context.MappingContextAwareBeanPostProcessor;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
@@ -101,6 +108,19 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition());
}
List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
if (customConvertersElements.size() == 1) {
Element customerConvertersElement = customConvertersElements.get(0);
ManagedList converterBeans = new ManagedList();
List<Element> listenerElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter");
if (listenerElements != null) {
for (Element listenerElement : listenerElements) {
converterBeans.add(parseConverter(listenerElement, parserContext));
}
}
converterBuilder.addPropertyValue("converters", converterBeans);
}
return converterBuilder.getBeanDefinition();
}
@@ -124,4 +144,24 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
return classes;
}
public BeanDefinition parseConverter(Element element, ParserContext parserContext) {
String converterRef= element.getAttribute("ref");
if (StringUtils.hasText(converterRef)) {
//TODO: need to make this work for beans not in the registry yet
BeanDefinition converterBean = parserContext.getRegistry().getBeanDefinition(converterRef);
return converterBean;
}
Element beanElement = DomUtils.getChildElementByTagName(element, "bean");
if (beanElement != null) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(beanElement);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDef);
return beanDef.getBeanDefinition();
}
parserContext.getReaderContext().error(
"Element <converter> must specify either 'ref' or contain a bean definition for the converter", element);
return null;
}
}

View File

@@ -1,12 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/mongo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:repository="http://www.springframework.org/schema/data/repository"
targetNamespace="http://www.springframework.org/schema/data/mongo"
elementFormDefault="qualified" attributeFormDefault="unqualified">
elementFormDefault="qualified" attributeFormDefault="unqualified"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/context"
schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd"/>
@@ -126,6 +132,22 @@ Defines a MongoConverter for getting rich mapping functionality.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="custom-converters" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Top-level element that contains one or more custom converters to be used for mapping
domain objects to and from Mongo's DBObject
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="converter" type="customConverterType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -260,5 +282,30 @@ This controls whether or not on a connect, the system retries automatically. De
</xsd:attribute>
</xsd:complexType>
<xsd:group name="beanElementGroup">
<xsd:choice>
<xsd:element ref="beans:bean" />
<xsd:element ref="beans:ref" />
</xsd:choice>
</xsd:group>
<xsd:complexType name="customConverterType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Element defining a custom converterr.
]]></xsd:documentation>
</xsd:annotation>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1" />
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A reference to a custom converter.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* Integration test for {@link MongoTemplate}.
*
* @author Oliver Gierke
* @author Thomas Risberg
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:template-mapping.xml")
public class MongoTemplateMappingTests {
@Autowired
@Qualifier("mongoTemplate1")
MongoTemplate template1;
@Autowired
@Qualifier("mongoTemplate2")
MongoTemplate template2;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() {
template1.dropCollection(template1.getCollectionName(Person.class));
}
@Test
public void insertsEntityCorrectly1() throws Exception {
addAndRetrievePerson(template1);
checkPersonPersisted(template1);
}
@Test
public void insertsEntityCorrectly2() throws Exception {
addAndRetrievePerson(template2);
checkPersonPersisted(template2);
}
private void addAndRetrievePerson(MongoTemplate template) {
Person person = new Person("Oliver");
person.setAge(25);
template.insert(person);
List<Person> result = template.find(new Query(Criteria.where("_id").is(person.getId())), Person.class);
assertThat(result.size(), is(1));
assertThat(result, hasItem(person));
assertThat(result.get(0).getFirstName(), is("Oliver"));
assertThat(result.get(0).getAge(), is(25));
}
private void checkPersonPersisted(MongoTemplate template) {
template.execute(Person.class, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
DBObject dbo = collection.findOne();
assertThat((String)dbo.get("name"), is("Oliver"));
return null;
}
});
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.data.document.mongodb;
import org.bson.types.ObjectId;
import org.springframework.core.convert.converter.Converter;
import com.mongodb.DBObject;
public class PersonReadConverter implements Converter<DBObject, Person> {
public Person convert(DBObject source) {
Person p = new Person((ObjectId)source.get("_id"), (String)source.get("name"));
p.setAge((Integer) source.get("age"));
return p;
}
}

View File

@@ -0,0 +1,18 @@
package org.springframework.data.document.mongodb;
import org.springframework.core.convert.converter.Converter;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
public class PersonWriteConverter implements Converter<Person, DBObject> {
public DBObject convert(Person source) {
DBObject dbo = new BasicDBObject();
dbo.put("_id", source.getId());
dbo.put("name", source.getFirstName());
dbo.put("age", source.getAge());
return dbo;
}
}

View File

@@ -12,5 +12,21 @@
<mongo:mongo id="defaultMongo" host="localhost" port="27017"/>
<bean id="readConverter" class="org.springframework.data.document.mongodb.PersonReadConverter"/>
<mongo:mapping-converter>
<mongo:custom-converters>
<mongo:converter ref="readConverter"/>
<mongo:converter>
<bean class="org.springframework.data.document.mongodb.PersonWriteConverter"/>
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg name="mongo" ref="mongo"/>
<constructor-arg name="databaseName" value="database"/>
<constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>
</beans>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
<mongo:mongo host="localhost" port="27017"/>
<bean id="mappingConverter1" class="org.springframework.data.document.mongodb.convert.MappingMongoConverter">
<constructor-arg ref="mappingContext" />
<property name="converters">
<list>
<bean class="org.springframework.data.document.mongodb.PersonReadConverter"/>
<bean class="org.springframework.data.document.mongodb.PersonWriteConverter"/>
</list>
</property>
</bean>
<bean id="mappingContext" class="org.springframework.data.document.mongodb.mapping.MongoMappingContext"/>
<bean id="mongoTemplate1" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg ref="mongo"/>
<constructor-arg name="databaseName" value="database"/>
<constructor-arg ref="mappingConverter1"/>
</bean>
<mongo:mapping-converter id="mappingConverter2" base-package="org.springframework.data.document.mongodb.mapping"
mongo-template-ref="mongoTemplate2">
<mongo:custom-converters>
<mongo:converter>
<bean class="org.springframework.data.document.mongodb.PersonReadConverter"/>
</mongo:converter>
<mongo:converter>
<bean class="org.springframework.data.document.mongodb.PersonWriteConverter"/>
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
<bean id="mongoTemplate2" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg ref="mongo"/>
<constructor-arg name="databaseName" value="database"/>
<constructor-arg ref="mappingConverter2"/>
</bean>
</beans>