Skip to content

Commit e0dda2a

Browse files
committed
DATACMNS-333 - Added Jackson 2 support to repository populators.
Added necessary infrastructure to use Jackson 2 with repository populators. Expose a jackson2-populator XML element in the namespace to setup a Jackson2 based repository populator.
1 parent cd2ea03 commit e0dda2a

File tree

10 files changed

+283
-7
lines changed

10 files changed

+283
-7
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
<properties>
2020
<jackson>1.9.7</jackson>
21+
<jackson2>2.2.2</jackson2>
2122
<springhateoas>0.6.0.BUILD-SNAPSHOT</springhateoas>
2223
<dist.key>DATACMNS</dist.key>
2324
</properties>
@@ -69,6 +70,12 @@
6970
<version>${jackson}</version>
7071
<optional>true</optional>
7172
</dependency>
73+
<dependency>
74+
<groupId>com.fasterxml.jackson.core</groupId>
75+
<artifactId>jackson-databind</artifactId>
76+
<version>${jackson2}</version>
77+
<optional>true</optional>
78+
</dependency>
7279
<dependency>
7380
<groupId>org.springframework</groupId>
7481
<artifactId>spring-web</artifactId>

src/main/java/org/springframework/data/repository/config/RepositoryNameSpaceHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ public class RepositoryNameSpaceHandler extends NamespaceHandlerSupport {
3636
public void init() {
3737
registerBeanDefinitionParser("unmarshaller-populator", PARSER);
3838
registerBeanDefinitionParser("jackson-populator", PARSER);
39+
registerBeanDefinitionParser("jackson2-populator", PARSER);
3940
}
4041
}

src/main/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParser.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,9 +15,12 @@
1515
*/
1616
package org.springframework.data.repository.config;
1717

18+
import java.util.Arrays;
19+
1820
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
1921
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
2022
import org.springframework.beans.factory.xml.BeanDefinitionParser;
23+
import org.springframework.data.repository.init.Jackson2RepositoryPopulatorFactoryBean;
2124
import org.springframework.data.repository.init.JacksonRepositoryPopulatorFactoryBean;
2225
import org.springframework.data.repository.init.UnmarshallerRepositoryPopulatorFactoryBean;
2326
import org.springframework.util.StringUtils;
@@ -38,8 +41,16 @@ public class ResourceReaderRepositoryPopulatorBeanDefinitionParser extends Abstr
3841
protected String getBeanClassName(Element element) {
3942

4043
String name = element.getLocalName();
41-
return "unmarshaller-populator".equals(name) ? UnmarshallerRepositoryPopulatorFactoryBean.class.getName()
42-
: JacksonRepositoryPopulatorFactoryBean.class.getName();
44+
45+
if ("unmarshaller-populator".equals(name)) {
46+
return UnmarshallerRepositoryPopulatorFactoryBean.class.getName();
47+
} else if ("jackson-populator".equals(name)) {
48+
return JacksonRepositoryPopulatorFactoryBean.class.getName();
49+
} else if ("jackson2-populator".equals(name)) {
50+
return Jackson2RepositoryPopulatorFactoryBean.class.getName();
51+
}
52+
53+
throw new IllegalStateException("Unsupported populator type " + name + "!");
4354
}
4455

4556
/*
@@ -55,7 +66,7 @@ protected void doParse(Element element, BeanDefinitionBuilder builder) {
5566

5667
if ("unmarshaller-populator".equals(localName)) {
5768
parseXmlPopulator(element, builder);
58-
} else if ("jackson-populator".equals(localName)) {
69+
} else if (Arrays.asList("jackson-populator", "jackson2-populator").contains(localName)) {
5970
parseJsonPopulator(element, builder);
6071
}
6172
}
@@ -66,7 +77,7 @@ protected void doParse(Element element, BeanDefinitionBuilder builder) {
6677
* @param element
6778
* @param builder
6879
*/
69-
private void parseJsonPopulator(Element element, BeanDefinitionBuilder builder) {
80+
private static void parseJsonPopulator(Element element, BeanDefinitionBuilder builder) {
7081

7182
String objectMapperRef = element.getAttribute("object-mapper-ref");
7283

@@ -81,7 +92,7 @@ private void parseJsonPopulator(Element element, BeanDefinitionBuilder builder)
8192
* @param element
8293
* @param builder
8394
*/
84-
private void parseXmlPopulator(Element element, BeanDefinitionBuilder builder) {
95+
private static void parseXmlPopulator(Element element, BeanDefinitionBuilder builder) {
8596

8697
String unmarshallerRefName = element.getAttribute("unmarshaller-ref");
8798

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.init;
17+
18+
import org.springframework.beans.factory.FactoryBean;
19+
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
22+
/**
23+
* {@link FactoryBean} to set up a {@link ResourceReaderRepositoryPopulator} with a {@link Jackson2ResourceReader}.
24+
*
25+
* @author Oliver Gierke
26+
* @since 1.6
27+
*/
28+
public class Jackson2RepositoryPopulatorFactoryBean extends AbstractRepositoryPopulatorFactoryBean {
29+
30+
private ObjectMapper mapper;
31+
32+
/**
33+
* Configures the {@link ObjectMapper} to be used.
34+
*
35+
* @param mapper
36+
*/
37+
public void setMapper(ObjectMapper mapper) {
38+
this.mapper = mapper;
39+
}
40+
41+
/*
42+
* (non-Javadoc)
43+
* @see org.springframework.data.repository.init.AbstractRepositoryPopulatorFactoryBean#getResourceReader()
44+
*/
45+
@Override
46+
protected ResourceReader getResourceReader() {
47+
return new Jackson2ResourceReader(mapper);
48+
}
49+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.init;
17+
18+
import static com.fasterxml.jackson.databind.DeserializationFeature.*;
19+
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.util.ArrayList;
23+
import java.util.Iterator;
24+
import java.util.List;
25+
26+
import org.springframework.core.io.Resource;
27+
import org.springframework.util.ClassUtils;
28+
29+
import com.fasterxml.jackson.databind.JsonNode;
30+
import com.fasterxml.jackson.databind.ObjectMapper;
31+
32+
/**
33+
* A {@link ResourceReader} using Jackson to read JSON into objects.
34+
*
35+
* @author Oliver Gierke
36+
* @since 1.6
37+
*/
38+
public class Jackson2ResourceReader implements ResourceReader {
39+
40+
private static final String DEFAULT_TYPE_KEY = "_class";
41+
private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper();
42+
43+
static {
44+
DEFAULT_MAPPER.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
45+
}
46+
47+
private final ObjectMapper mapper;
48+
private String typeKey = DEFAULT_TYPE_KEY;
49+
50+
/**
51+
* Creates a new {@link Jackson2ResourceReader}.
52+
*/
53+
public Jackson2ResourceReader() {
54+
this(DEFAULT_MAPPER);
55+
}
56+
57+
/**
58+
* Creates a new {@link Jackson2ResourceReader} using the given {@link ObjectMapper}.
59+
*
60+
* @param mapper
61+
*/
62+
public Jackson2ResourceReader(ObjectMapper mapper) {
63+
this.mapper = mapper == null ? DEFAULT_MAPPER : mapper;
64+
}
65+
66+
/**
67+
* Configures the JSON document's key to lookup the type to instantiate the object. Defaults to
68+
* {@link Jackson2ResourceReader#DEFAULT_TYPE_KEY}.
69+
*
70+
* @param typeKey
71+
*/
72+
public void setTypeKey(String typeKey) {
73+
this.typeKey = typeKey;
74+
}
75+
76+
/*
77+
* (non-Javadoc)
78+
* @see org.springframework.data.repository.init.ResourceReader#readFrom(org.springframework.core.io.Resource, java.lang.ClassLoader)
79+
*/
80+
public Object readFrom(Resource resource, ClassLoader classLoader) throws Exception {
81+
82+
InputStream stream = resource.getInputStream();
83+
JsonNode node = mapper.reader(JsonNode.class).readTree(stream);
84+
85+
if (node.isArray()) {
86+
87+
Iterator<JsonNode> elements = node.elements();
88+
List<Object> result = new ArrayList<Object>();
89+
90+
while (elements.hasNext()) {
91+
JsonNode element = elements.next();
92+
result.add(readSingle(element, classLoader));
93+
}
94+
95+
return result;
96+
}
97+
98+
return readSingle(node, classLoader);
99+
}
100+
101+
/**
102+
* Reads the given {@link JsonNode} into an instance of the type encoded in it using the configured type key.
103+
*
104+
* @param node must not be {@literal null}.
105+
* @param classLoader
106+
* @return
107+
*/
108+
private Object readSingle(JsonNode node, ClassLoader classLoader) throws IOException {
109+
110+
JsonNode typeNode = node.findValue(typeKey);
111+
String typeName = typeNode == null ? null : typeNode.asText();
112+
113+
Class<?> type = ClassUtils.resolveClassName(typeName, classLoader);
114+
115+
return mapper.reader(type).readValue(node);
116+
}
117+
}

src/main/resources/org/springframework/data/repository/config/spring-repository-1.6.xsd

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@
9595
</xsd:annotation>
9696
<xsd:union memberTypes="xsd:string" />
9797
</xsd:simpleType>
98+
99+
<!-- JSON (Jackson2) initializer -->
100+
101+
<xsd:element name="jackson2-populator">
102+
<xsd:complexType>
103+
<xsd:complexContent>
104+
<xsd:extension base="populator">
105+
<xsd:attribute name="object-mapper-ref" type="objectMapper2Type" />
106+
</xsd:extension>
107+
</xsd:complexContent>
108+
</xsd:complexType>
109+
</xsd:element>
110+
111+
<xsd:simpleType name="objectMapper2Type">
112+
<xsd:annotation>
113+
<xsd:appinfo>
114+
<tool:expected-type type="com.fasterxml.jackson.databind.ObjectMapper" />
115+
</xsd:appinfo>
116+
</xsd:annotation>
117+
<xsd:union memberTypes="xsd:string" />
118+
</xsd:simpleType>
98119

99120
<xsd:attributeGroup name="repository-attributes">
100121
<xsd:attribute name="repository-impl-postfix" type="xsd:string"/>

src/test/java/org/springframework/data/repository/config/ResourceReaderRepositoryPopulatorBeanDefinitionParserIntegrationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2626
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
2727
import org.springframework.core.io.ClassPathResource;
28+
import org.springframework.data.repository.init.Jackson2ResourceReader;
2829
import org.springframework.data.repository.init.JacksonResourceReader;
2930
import org.springframework.data.repository.init.ResourceReaderRepositoryPopulator;
3031
import org.springframework.data.repository.init.UnmarshallingResourceReader;
@@ -60,6 +61,28 @@ public void registersJacksonInitializerCorrectly() {
6061
assertIsListOfClasspathResourcesWithPath(resources, "org/springframework/data/repository/init/data.json");
6162
}
6263

64+
/**
65+
* @see DATACMNS-333
66+
*/
67+
@Test
68+
public void registersJackson2InitializerCorrectly() {
69+
70+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
71+
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
72+
reader.loadBeanDefinitions(new ClassPathResource("populators.xml", getClass()));
73+
74+
BeanDefinition definition = beanFactory.getBeanDefinition("jackson2-populator");
75+
assertThat(definition, is(notNullValue()));
76+
77+
Object bean = beanFactory.getBean("jackson2-populator");
78+
assertThat(bean, is(instanceOf(ResourceReaderRepositoryPopulator.class)));
79+
Object resourceReader = ReflectionTestUtils.getField(bean, "reader");
80+
assertThat(resourceReader, is(instanceOf(Jackson2ResourceReader.class)));
81+
82+
Object resources = ReflectionTestUtils.getField(bean, "resources");
83+
assertIsListOfClasspathResourcesWithPath(resources, "org/springframework/data/repository/init/data.json");
84+
}
85+
6386
/**
6487
* @see DATACMNS-58
6588
*/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.init;
17+
18+
import static org.hamcrest.Matchers.*;
19+
import static org.junit.Assert.*;
20+
21+
import java.util.Collection;
22+
23+
import org.junit.Test;
24+
import org.springframework.core.io.ClassPathResource;
25+
26+
/**
27+
* Integration tests for {@link JacksonResourceReader}.
28+
*
29+
* @author Oliver Gierke
30+
* @since 1.6
31+
*/
32+
public class Jackson2ResourceReaderIntegrationTests {
33+
34+
@Test
35+
public void readsFileWithMultipleObjects() throws Exception {
36+
37+
ResourceReader reader = new Jackson2ResourceReader();
38+
Object result = reader.readFrom(new ClassPathResource("data.json", getClass()), null);
39+
40+
assertThat(result, is(instanceOf(Collection.class)));
41+
assertThat((Collection<?>) result, hasSize(1));
42+
}
43+
}

src/test/resources/org/springframework/data/repository/config/populators.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
<beans xmlns="http://www.springframework.org/schema/beans"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:repository="http://www.springframework.org/schema/data/repository"
44
xmlns:oxm="http://www.springframework.org/schema/oxm"
5-
xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd
5+
xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd
66
http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd
77
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
88

99
<repository:jackson-populator id="jackson-populator"
1010
locations="classpath:org/springframework/data/repository/init/data.json" />
1111

12+
<repository:jackson2-populator id="jackson2-populator"
13+
locations="classpath:org/springframework/data/repository/init/data.json" />
14+
1215
<repository:unmarshaller-populator
1316
id="xml-populator" locations="classpath:org/springframework/data/repository/init/data.xml"
1417
unmarshaller-ref="unmarshaller" />

template.mf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Bundle-ManifestVersion: 2
55
Import-Package:
66
sun.reflect;version="0";resolution:=optional
77
Import-Template:
8+
com.fasterxml.jackson.databind.*;version="${jackson2:[=.=.=,+1.0.0)}";resolution:=optional,
89
com.mysema.query.*;version="${querydsl:[=.=.=,+1.0.0)}";resolution:=optional,
910
javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional,
1011
javax.inject.*;version="[1.0.0,2.0.0)";resolution:=optional,

0 commit comments

Comments
 (0)