From d441d9519709d5e44773474bb9c541cf9b41ad86 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 21 Jun 2012 19:32:07 +0200 Subject: [PATCH] DATACMNS-58 - Added support for repository populators. Added RepositoryPopulator abstraction and implementations based on Spring OXM Unmarshallers as well as Jackson. This allows arbitrary repositories being populated with data pulled from XML / JSON, no matter what store they are actually backed. The populator will eventually populate the repositories held in a Repositories instance. It can be used like this: Repositories repositories = new Repositories(applicationContext); ResourceReader reader = new JacksonResourceReader(); ResourceReaderRepositoryPopulator populator = new ResourceReaderRepositoryPopulator(reader); populator.setResourceLocation("classpath*:data.json"); populator.populate(repositories); The ResourceReader defines what technology shall be used to read the data from the file into objects. The ResourceReaderRepositoryPopulator uses the reader and can either get a set of Resource instances configured or is able to lookup resources using a location string. The actual Repositories instance captures all CrudRepository instances contained inside an ApplicationContext. The populators can also be used from within XML configuration though the repository namespace elements shown below: Updated reference documentation accordingly. --- spring-data-commons-core/pom.xml | 13 ++ .../config/RepositoryNameSpaceHandler.java | 40 ++++ ...positoryPopulatorBeanDefinitionParser.java | 101 +++++++++ ...bstractRepositoryPopulatorFactoryBean.java | 82 +++++++ ...JacksonRepositoryPopulatorFactoryBean.java | 47 ++++ .../init/JacksonResourceReader.java | 114 ++++++++++ .../repository/init/RepositoryPopulator.java | 34 +++ .../data/repository/init/ResourceReader.java | 30 +++ .../ResourceReaderRepositoryPopulator.java | 143 +++++++++++++ ...shallerRepositoryPopulatorFactoryBean.java | 57 +++++ .../init/UnmarshallingResourceReader.java | 47 ++++ .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 3 +- .../main/resources/META-INF/spring.tooling | 4 + .../config/spring-repository-1.4.xsd | 202 ++++++++++++++++++ ...rBeanDefinitionParserIntegrationTests.java | 78 +++++++ ...JacksonResourceReaderIntegrationTests.java | 40 ++++ .../data/repository/init/Person.java | 25 +++ ...eReaderRepositoryInitializerUnitTests.java | 80 +++++++ .../data/repository/config/jaxb.index | 0 .../data/repository/config/populators.xml | 18 ++ .../data/repository/init/data.json | 3 + spring-data-commons-core/template.mf | 5 +- src/docbkx/repositories.xml | 83 +++++++ 24 files changed, 1248 insertions(+), 2 deletions(-) create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryNameSpaceHandler.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParser.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/AbstractRepositoryPopulatorFactoryBean.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonRepositoryPopulatorFactoryBean.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonResourceReader.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/RepositoryPopulator.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReader.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallerRepositoryPopulatorFactoryBean.java create mode 100644 spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallingResourceReader.java create mode 100644 spring-data-commons-core/src/main/resources/META-INF/spring.handlers create mode 100644 spring-data-commons-core/src/main/resources/META-INF/spring.tooling create mode 100644 spring-data-commons-core/src/main/resources/org/springframework/data/repository/config/spring-repository-1.4.xsd create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/repository/init/JacksonResourceReaderIntegrationTests.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/repository/init/Person.java create mode 100644 spring-data-commons-core/src/test/java/org/springframework/data/repository/init/ResourceReaderRepositoryInitializerUnitTests.java create mode 100644 spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/jaxb.index create mode 100644 spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/populators.xml create mode 100644 spring-data-commons-core/src/test/resources/org/springframework/data/repository/init/data.json diff --git a/spring-data-commons-core/pom.xml b/spring-data-commons-core/pom.xml index a2a1a2d18..cfc37809b 100644 --- a/spring-data-commons-core/pom.xml +++ b/spring-data-commons-core/pom.xml @@ -16,6 +16,7 @@ 2.5.0 1.0 1.1.3 + 1.9.7 @@ -32,6 +33,18 @@ ${org.springframework.version} true + + org.springframework + spring-oxm + ${org.springframework.version} + true + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + true + org.springframework spring-web diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryNameSpaceHandler.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryNameSpaceHandler.java new file mode 100644 index 000000000..7c314088d --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/RepositoryNameSpaceHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 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.repository.config; + +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.NamespaceHandler; +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * {@link NamespaceHandler} to register {@link BeanDefinitionParser}s for repository initializers. + * + * @author Oliver Gierke + * @since 1.4 + */ +public class RepositoryNameSpaceHandler extends NamespaceHandlerSupport { + + private static final BeanDefinitionParser PARSER = new ResourceReaderRepositoryPopulatorBeanDefinitionParser(); + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.NamespaceHandler#init() + */ + public void init() { + registerBeanDefinitionParser("unmarshaller-populator", PARSER); + registerBeanDefinitionParser("jackson-populator", PARSER); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParser.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParser.java new file mode 100644 index 000000000..598790c1b --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParser.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012 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.repository.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.data.repository.init.JacksonRepositoryPopulatorFactoryBean; +import org.springframework.data.repository.init.UnmarshallerRepositoryPopulatorFactoryBean; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * {@link BeanDefinitionParser} to parse repository initializers. + * + * @author Oliver Gierke + */ +public class ResourceReaderRepositoryPopulatorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClassName(org.w3c.dom.Element) + */ + @Override + protected String getBeanClassName(Element element) { + + String name = element.getLocalName(); + return "unmarshaller-populator".equals(name) ? UnmarshallerRepositoryPopulatorFactoryBean.class.getName() + : JacksonRepositoryPopulatorFactoryBean.class.getName(); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) + */ + @Override + protected void doParse(Element element, BeanDefinitionBuilder builder) { + + String localName = element.getLocalName(); + + builder.addPropertyValue("resources", element.getAttribute("locations")); + + if ("unmarshaller-populator".equals(localName)) { + parseXmlPopulator(element, builder); + } else if ("jackson-populator".equals(localName)) { + parseJsonPopulator(element, builder); + } + } + + /** + * Populates the {@link BeanDefinitionBuilder} for a Jackson reader. + * + * @param element + * @param builder + */ + private void parseJsonPopulator(Element element, BeanDefinitionBuilder builder) { + + String objectMapperRef = element.getAttribute("object-mapper-ref"); + + if (StringUtils.hasText(objectMapperRef)) { + builder.addPropertyReference("mapper", objectMapperRef); + } + } + + /** + * Populate the {@link BeanDefinitionBuilder} for XML reader. + * + * @param element + * @param builder + */ + private void parseXmlPopulator(Element element, BeanDefinitionBuilder builder) { + + String unmarshallerRefName = element.getAttribute("unmarshaller-ref"); + + if (StringUtils.hasText(unmarshallerRefName)) { + builder.addPropertyReference("unmarshaller", unmarshallerRefName); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateIdAsFallback() + */ + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/AbstractRepositoryPopulatorFactoryBean.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/AbstractRepositoryPopulatorFactoryBean.java new file mode 100644 index 000000000..0276019cf --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/AbstractRepositoryPopulatorFactoryBean.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012 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.repository.init; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.io.Resource; +import org.springframework.data.repository.support.Repositories; + +/** + * {@link FactoryBean} to set up a {@link ResourceReaderRepositoryPopulator} with a {@link JacksonResourceReader}. + * + * @author Oliver Gierke + */ +public abstract class AbstractRepositoryPopulatorFactoryBean extends + AbstractFactoryBean implements ApplicationListener { + + private Resource[] resources; + private RepositoryPopulator populator; + + /** + * Configures the {@link Resource}s to be used to load objects from and initialize the repositories eventually. + * + * @param resources the resources to set + */ + public void setResources(Resource[] resources) { + this.resources = resources; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType() + */ + @Override + public Class getObjectType() { + return ResourceReaderRepositoryPopulator.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() + */ + @Override + protected ResourceReaderRepositoryPopulator createInstance() throws Exception { + + ResourceReaderRepositoryPopulator initializer = new ResourceReaderRepositoryPopulator(getResourceReader()); + initializer.setResources(resources); + + this.populator = initializer; + + return initializer; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + public void onApplicationEvent(ContextRefreshedEvent event) { + + if (event.equals(getBeanFactory())) { + Repositories repositories = new Repositories(event.getApplicationContext()); + populator.populate(repositories); + } + } + + protected abstract ResourceReader getResourceReader(); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonRepositoryPopulatorFactoryBean.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonRepositoryPopulatorFactoryBean.java new file mode 100644 index 000000000..bebd231f0 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonRepositoryPopulatorFactoryBean.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 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.repository.init; + +import org.codehaus.jackson.map.ObjectMapper; +import org.springframework.beans.factory.FactoryBean; + +/** + * {@link FactoryBean} to set up a {@link ResourceReaderRepositoryPopulator} with a {@link JacksonResourceReader}. + * + * @author Oliver Gierke + */ +public class JacksonRepositoryPopulatorFactoryBean extends AbstractRepositoryPopulatorFactoryBean { + + private ObjectMapper mapper; + + /** + * Configures the {@link ObjectMapper} to be used. + * + * @param mapper + */ + public void setMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.init.AbstractRepositoryPopulatorFactoryBean#getResourceReader() + */ + @Override + protected ResourceReader getResourceReader() { + return new JacksonResourceReader(mapper); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonResourceReader.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonResourceReader.java new file mode 100644 index 000000000..98adfdb3d --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/JacksonResourceReader.java @@ -0,0 +1,114 @@ +/* + * Copyright 2012 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.repository.init; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.DeserializationConfig.Feature; +import org.codehaus.jackson.map.ObjectMapper; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; + +/** + * A {@link ResourceReader} using Jackson to read JSON into objects. + * + * @author Oliver Gierke + */ +public class JacksonResourceReader implements ResourceReader { + + private static final String DEFAULT_TYPE_KEY = "_class"; + private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper(); + + static { + DEFAULT_MAPPER.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + private final ObjectMapper mapper; + private String typeKey = DEFAULT_TYPE_KEY; + + /** + * Creates a new {@link JacksonResourceReader}. + */ + public JacksonResourceReader() { + this(DEFAULT_MAPPER); + } + + /** + * Creates a new {@link JacksonResourceReader} using the given {@link ObjectMapper}. + * + * @param mapper + */ + public JacksonResourceReader(ObjectMapper mapper) { + this.mapper = mapper == null ? DEFAULT_MAPPER : mapper; + } + + /** + * Configures the JSON document's key to lookup the type to instantiate the object. Defaults to + * {@value JacksonResourceReader#DEFAULT_TYPE_KEY}. + * + * @param typeKey + */ + public void setTypeKey(String typeKey) { + this.typeKey = typeKey; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.init.ResourceReader#readFrom(org.springframework.core.io.Resource, java.lang.ClassLoader) + */ + public Object readFrom(Resource resource, ClassLoader classLoader) throws Exception { + + InputStream stream = resource.getInputStream(); + JsonNode node = mapper.reader(JsonNode.class).readTree(stream); + + if (node.isArray()) { + + Iterator elements = node.getElements(); + List result = new ArrayList(); + + while (elements.hasNext()) { + JsonNode element = elements.next(); + result.add(readSingle(element, classLoader)); + } + + return result; + } + + return readSingle(node, classLoader); + } + + /** + * Reads the given {@link JsonNode} into an instance of the type encoded in it using the configured type key. + * + * @param node must not be {@literal null}. + * @param classLoader + * @return + */ + private Object readSingle(JsonNode node, ClassLoader classLoader) throws IOException { + + JsonNode typeNode = node.findValue(typeKey); + String typeName = typeNode == null ? null : typeNode.asText(); + + Class type = ClassUtils.resolveClassName(typeName, classLoader); + + return mapper.reader(type).readValue(node); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/RepositoryPopulator.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/RepositoryPopulator.java new file mode 100644 index 000000000..252e15534 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/RepositoryPopulator.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012 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.repository.init; + +import org.springframework.data.repository.support.Repositories; + +/** + * Interface for components that will populate the Spring Data repositories with objects. + * + * @author Oliver Gierke + * @since 1.4 + */ +public interface RepositoryPopulator { + + /** + * Populates the given {@link Repositories}. + * + * @param repositories + */ + void populate(Repositories repositories); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReader.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReader.java new file mode 100644 index 000000000..acb892d7b --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReader.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012 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.repository.init; + +import org.springframework.core.io.Resource; + +/** + * @author Oliver Gierke + */ +public interface ResourceReader { + + public static enum Type { + XML, JSON; + } + + Object readFrom(Resource resource, ClassLoader classLoader) throws Exception; +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java new file mode 100644 index 000000000..04285ef1f --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012 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.repository.init; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.support.Repositories; +import org.springframework.util.Assert; + +/** + * A {@link RepositoryPopulator} using a {@link ResourceReader} to read objects from the configured {@link Resource} + * s. + * + * @author Oliver Gierke + * @since 1.4 + */ +public class ResourceReaderRepositoryPopulator implements RepositoryPopulator { + + private static final Log LOG = LogFactory.getLog(ResourceReaderRepositoryPopulator.class); + + private final ResourcePatternResolver resolver; + private final ResourceReader reader; + private final ClassLoader classLoader; + + private Collection resources; + + /** + * Creates a new {@link ResourceReaderRepositoryPopulator} using the given {@link ResourceReader}. + * + * @param reader must not be {@literal null}. + */ + public ResourceReaderRepositoryPopulator(ResourceReader reader) { + this(reader, null); + } + + /** + * Createsa a new {@link ResourceReaderRepositoryPopulator} using the given {@link ResourceReader} and + * {@link ClassLoader}. + * + * @param reader must not be {@literal null}. + * @param classLoader + */ + public ResourceReaderRepositoryPopulator(ResourceReader resourceReader, ClassLoader classLoader) { + + Assert.notNull(resourceReader); + + this.reader = resourceReader; + this.classLoader = classLoader; + this.resolver = classLoader == null ? new PathMatchingResourcePatternResolver() + : new PathMatchingResourcePatternResolver(classLoader); + } + + /** + * Configures the location of the {@link Resource}s to be used to initialize the repositories. + * + * @param location must not be {@literal null} or empty. + * @throws IOException + */ + public void setResourceLocation(String location) throws IOException { + Assert.hasText(location); + setResources(resolver.getResources(location)); + } + + /** + * Configures the {@link Resource}s to be used to initialize the repositories. + * + * @param resources + */ + public void setResources(Resource... resources) { + this.resources = Arrays.asList(resources); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.init.RepositoryPopulator#initialize() + */ + public void populate(Repositories repositories) { + + for (Resource resource : resources) { + + Object result = readObjectFrom(resource); + + if (result instanceof Collection) { + for (Object element : (Collection) result) { + if (element != null) { + persist(element, repositories); + } else { + LOG.info("Skipping null element found in unmarshal result!"); + } + } + } else { + persist(result, repositories); + } + } + } + + /** + * Reads the given resource into an {@link Object} using the configured {@link ResourceReader}. + * + * @param resource must not be {@literal null}. + * @return + */ + private Object readObjectFrom(Resource resource) { + try { + return reader.readFrom(resource, classLoader); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Persists the given {@link Object} using a suitable repository. + * + * @param object must not be {@literal null}. + * @param repositories must not be {@literal null}. + */ + private void persist(Object object, Repositories repositories) { + CrudRepository repository = repositories.getRepositoryFor(object.getClass()); + repository.save(object); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallerRepositoryPopulatorFactoryBean.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallerRepositoryPopulatorFactoryBean.java new file mode 100644 index 000000000..0307c66d3 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallerRepositoryPopulatorFactoryBean.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 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.repository.init; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.oxm.Unmarshaller; +import org.springframework.util.Assert; + +/** + * {@link FactoryBean} to create a {@link ResourceReaderRepositoryPopulator} using an {@link Unmarshaller}. + * + * @author Oliver Gierke + */ +public class UnmarshallerRepositoryPopulatorFactoryBean extends AbstractRepositoryPopulatorFactoryBean { + + private Unmarshaller unmarshaller; + + /** + * Configures the {@link Unmarshaller} to be used. + * + * @param unmarshaller the unmarshaller to set + */ + public void setUnmarshaller(Unmarshaller unmarshaller) { + this.unmarshaller = unmarshaller; + } + + /* (non-Javadoc) + * @see org.springframework.data.repository.init.AbstractRepositoryPopulatorFactoryBean#getResourceReader() + */ + @Override + protected ResourceReader getResourceReader() { + return new UnmarshallingResourceReader(unmarshaller); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet() + */ + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(unmarshaller, "No Unmarshaller configured!"); + super.afterPropertiesSet(); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallingResourceReader.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallingResourceReader.java new file mode 100644 index 000000000..b6d8d3d2f --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/init/UnmarshallingResourceReader.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 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.repository.init; + +import java.io.IOException; + +import javax.xml.transform.stream.StreamSource; + +import org.springframework.core.io.Resource; +import org.springframework.oxm.Unmarshaller; + +/** + * @author Oliver Gierke + */ +public class UnmarshallingResourceReader implements ResourceReader { + + private final Unmarshaller unmarshaller; + + /** + * @param unmarshaller + */ + public UnmarshallingResourceReader(Unmarshaller unmarshaller) { + this.unmarshaller = unmarshaller; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.init.ResourceReader#readFrom(org.springframework.core.io.Resource, java.lang.ClassLoader) + */ + public Object readFrom(Resource resource, ClassLoader classLoader) throws IOException { + StreamSource source = new StreamSource(resource.getInputStream()); + return unmarshaller.unmarshal(source); + } +} diff --git a/spring-data-commons-core/src/main/resources/META-INF/spring.handlers b/spring-data-commons-core/src/main/resources/META-INF/spring.handlers new file mode 100644 index 000000000..685424493 --- /dev/null +++ b/spring-data-commons-core/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/data/repository=org.springframework.data.repository.config.RepositoryNameSpaceHandler diff --git a/spring-data-commons-core/src/main/resources/META-INF/spring.schemas b/spring-data-commons-core/src/main/resources/META-INF/spring.schemas index 22d3f4bfa..95afeab10 100644 --- a/spring-data-commons-core/src/main/resources/META-INF/spring.schemas +++ b/spring-data-commons-core/src/main/resources/META-INF/spring.schemas @@ -1,2 +1,3 @@ http\://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd=org/springframework/data/repository/config/spring-repository-1.0.xsd -http\://www.springframework.org/schema/data/repository/spring-repository.xsd=org/springframework/data/repository/config/spring-repository-1.0.xsd +http\://www.springframework.org/schema/data/repository/spring-repository-1.4.xsd=org/springframework/data/repository/config/spring-repository-1.4.xsd +http\://www.springframework.org/schema/data/repository/spring-repository.xsd=org/springframework/data/repository/config/spring-repository-1.4.xsd diff --git a/spring-data-commons-core/src/main/resources/META-INF/spring.tooling b/spring-data-commons-core/src/main/resources/META-INF/spring.tooling new file mode 100644 index 000000000..2edb997e7 --- /dev/null +++ b/spring-data-commons-core/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,4 @@ +# Tooling related information for the repository namespace +http\://www.springframework.org/schema/data/repository@name=Repository Namespace +http\://www.springframework.org/schema/data/repository@prefix=repository +http\://www.springframework.org/schema/data/repository@icon=org/springframework/jdbc/config/spring-jdbc.gif diff --git a/spring-data-commons-core/src/main/resources/org/springframework/data/repository/config/spring-repository-1.4.xsd b/spring-data-commons-core/src/main/resources/org/springframework/data/repository/config/spring-repository-1.4.xsd new file mode 100644 index 000000000..785fb6d3a --- /dev/null +++ b/spring-data-commons-core/src/main/resources/org/springframework/data/repository/config/spring-repository-1.4.xsd @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Declares a single DAO instance. + + + + + + + + + + + + + + + + Where to find the files to read the objects from the repository shall be populated with. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests.java new file mode 100644 index 000000000..9faf6706c --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012 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.repository.config; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.repository.init.JacksonResourceReader; +import org.springframework.data.repository.init.ResourceReaderRepositoryPopulator; +import org.springframework.data.repository.init.UnmarshallingResourceReader; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * Integratin tests for the initializer namespace elements. + * + * @author Oliver Gierke + */ +public class ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests { + + /** + * @see DATACMNS-58 + */ + @Test + public void registersJacksonInitializerCorrectly() { + + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); + reader.loadBeanDefinitions(new ClassPathResource("populators.xml", getClass())); + + BeanDefinition definition = beanFactory.getBeanDefinition("jackson-populator"); + assertThat(definition, is(notNullValue())); + + Object bean = beanFactory.getBean("jackson-populator"); + assertThat(bean, is(instanceOf(ResourceReaderRepositoryPopulator.class))); + Object resourceReader = ReflectionTestUtils.getField(bean, "reader"); + assertThat(resourceReader, is(instanceOf(JacksonResourceReader.class))); + } + + /** + * @see DATACMNS-58 + */ + @Test + public void registersXmlInitializerCorrectly() { + + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); + reader.loadBeanDefinitions(new ClassPathResource("populators.xml", getClass())); + + BeanDefinition definition = beanFactory.getBeanDefinition("xml-populator"); + assertThat(definition, is(notNullValue())); + + Object bean = beanFactory.getBean("xml-populator"); + assertThat(bean, is(instanceOf(ResourceReaderRepositoryPopulator.class))); + Object resourceReader = ReflectionTestUtils.getField(bean, "reader"); + assertThat(resourceReader, is(instanceOf(UnmarshallingResourceReader.class))); + Object unmarshaller = ReflectionTestUtils.getField(resourceReader, "unmarshaller"); + assertThat(unmarshaller, is(instanceOf(Jaxb2Marshaller.class))); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/JacksonResourceReaderIntegrationTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/JacksonResourceReaderIntegrationTests.java new file mode 100644 index 000000000..55c36c183 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/JacksonResourceReaderIntegrationTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 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.repository.init; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Collection; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +/** + * @author Oliver Gierke + */ +public class JacksonResourceReaderIntegrationTests { + + @Test + public void readsFileWithMultipleObjects() throws Exception { + + ResourceReader reader = new JacksonResourceReader(); + Object result = reader.readFrom(new ClassPathResource("data.json", getClass()), null); + + assertThat(result, is(instanceOf(Collection.class))); + assertThat((Collection) result, hasSize(1)); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/Person.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/Person.java new file mode 100644 index 000000000..b17d8afd9 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/Person.java @@ -0,0 +1,25 @@ +/* + * Copyright 2012 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.repository.init; + +/** + * @author Oliver Gierke + */ +public class Person { + + String firstname; + String lastname; +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/ResourceReaderRepositoryInitializerUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/ResourceReaderRepositoryInitializerUnitTests.java new file mode 100644 index 000000000..0bea8b790 --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/init/ResourceReaderRepositoryInitializerUnitTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 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.repository.init; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.io.Resource; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.support.Repositories; + +/** + * Unit tests for {@link UnmarshallingRepositoryInitializer}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class ResourceReaderRepositoryInitializerUnitTests { + + @Mock + ResourceReader reader; + @Mock + Repositories repositories; + @Mock + Resource resource; + @Mock + CrudRepository repo; + + @Test + public void storesSingleObjectCorrectly() throws Exception { + + Object reference = new Object(); + + setUpReferenceAndInititalize(reference); + + verify(repo, times(1)).save(reference); + } + + @Test + public void storesCollectionOfObjectsCorrectly() throws Exception { + + Object object = new Object(); + Collection reference = Collections.singletonList(object); + + setUpReferenceAndInititalize(reference); + + verify(repo, times(1)).save(object); + } + + private void setUpReferenceAndInititalize(Object reference) throws Exception { + + when(reader.readFrom(any(Resource.class), any(ClassLoader.class))).thenReturn(reference); + when(repositories.getRepositoryFor(Object.class)).thenReturn(repo); + + ResourceReaderRepositoryPopulator initializer = new ResourceReaderRepositoryPopulator(reader); + initializer.setResources(resource); + initializer.populate(repositories); + } +} diff --git a/spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/jaxb.index b/spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/jaxb.index new file mode 100644 index 000000000..e69de29bb diff --git a/spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/populators.xml b/spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/populators.xml new file mode 100644 index 000000000..62643d683 --- /dev/null +++ b/spring-data-commons-core/src/test/resources/org/springframework/data/repository/config/populators.xml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/spring-data-commons-core/src/test/resources/org/springframework/data/repository/init/data.json b/spring-data-commons-core/src/test/resources/org/springframework/data/repository/init/data.json new file mode 100644 index 000000000..5f977afbd --- /dev/null +++ b/spring-data-commons-core/src/test/resources/org/springframework/data/repository/init/data.json @@ -0,0 +1,3 @@ +[ { "_class" : "org.springframework.data.repository.init.Person", + "firstname" : "Dave", + "lastname" : "Matthews" } ] \ No newline at end of file diff --git a/spring-data-commons-core/template.mf b/spring-data-commons-core/template.mf index d2332be3f..166033ad7 100644 --- a/spring-data-commons-core/template.mf +++ b/spring-data-commons-core/template.mf @@ -8,16 +8,19 @@ Import-Template: com.mysema.query.*;version="[2.2.0,3.0.0)";resolution:=optional, javax.enterprise.*;version="${cdi.version:[=.=.=,+1.0.0)}";resolution:=optional, javax.inject.*;version="[1.0.0,2.0.0)";resolution:=optional, + javax.xml.transform.*;version="0";resolution:=optional, + org.codehaus.jackson.*;version="${jackson.version:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.aop.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.beans.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}", org.springframework.core.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}", org.springframework.context.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.dao.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, - org.springframework.util.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}", org.springframework.expression.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.expression.spel.standard.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.expression.spel.support.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, + org.springframework.oxm.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.transaction.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, + org.springframework.util.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}", org.springframework.validation.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.springframework.web.*;version="${org.springframework.version.30:[=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, diff --git a/src/docbkx/repositories.xml b/src/docbkx/repositories.xml index 4de861e7f..a3571503a 100644 --- a/src/docbkx/repositories.xml +++ b/src/docbkx/repositories.xml @@ -1028,5 +1028,88 @@ public class UserController { + +
+ Repository populators + + If you have been working with the JDBC module of Spring you're + probably familiar with the support to populate a DataSource using SQL + scripts. A similar abstraction is available on the repositories level + although we don't use SQL as data definition language as we need to be + store independent of course. Thus the populators support XML (through + Spring's OXM abstraction) and JSON (through Jackson) to define data for + the repositories to be populated with. + + Assume you have a file data.json with the + following content: + + + Data defined in JSON + + [ { "_class" : "com.acme.Person", + "firstname" : "Dave", + "lastname" : "Matthews" }, + { "_class" : "com.acme.Person", + "firstname" : "Carter", + "lastname" : "Beauford" } ] + + + You can easily populate you repositories by using the populator + elements of the repository namespace provided in Spring Data Commons. To + get the just shown data be populated to your + PersonRepository all you need to do is + the following: + + + Declaring a Jackson repository populator + + <?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:repository="http://www.springframework.org/schema/data/repository" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/repository + http://www.springframework.org/schema/data/repository/spring-repository.xsd"> + + <repository:jackson-populator location="classpath:data.json" /> + +</beans> + + + This declaration causes the data.json file being read, + deserialized by a Jackson ObjectMapper. The type + the JSON object will be unmarshalled to will be determined by inspecting + the _class attribute of the JSON document. We will + eventually select the appropriate repository being able to handle the + object just deserialized. + + To rather use XML to define the repositories shall be populated + with you can use the unmarshaller-populator you hand one of the + marshaller options Spring OXM provides you with. + + + Declaring an unmarshalling repository populator (using + JAXB) + + <?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:repository="http://www.springframework.org/schema/data/repository" + xmlns:oxm="http://www.springframework.org/schema/oxm" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/repository + http://www.springframework.org/schema/data/repository/spring-repository.xsd + http://www.springframework.org/schema/oxm + http://www.springframework.org/schema/oxm/spring-oxm.xsd"> + + <repository:unmarshaller-populator location="classpath:data.json" unmarshaller-ref="unmarshaller" /> + + <oxm:jaxb2-marshaller contextPath="com.acme" /> + +</beans> + +