Skip to content

Commit d669ddc

Browse files
committed
DATAREST-1256 - Upgraded to Spring HATEOAS 0.25.0.BUILD-SNAPSHOT.
Adapted RepositoryRestMvcConfiguration to now create a shared fallback ObjectMapper and avoid to register it as Spring Bean. Adapted test cases that previously were relying on such a bean being present. Adapted test cases to verify on the correct ALPS document structure (see spring-projects/spring-hateoas#665).
1 parent 06f775a commit d669ddc

File tree

8 files changed

+91
-51
lines changed

8 files changed

+91
-51
lines changed

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<springdata.solr>3.0.9.BUILD-SNAPSHOT</springdata.solr>
3636
<springdata.cassandra>2.0.9.BUILD-SNAPSHOT</springdata.cassandra>
3737
<springdata.keyvalue>2.0.9.BUILD-SNAPSHOT</springdata.keyvalue>
38+
<spring-hateoas>0.25.0.BUILD-SNAPSHOT</spring-hateoas>
3839

3940
<hibernate.version>4.3.10.Final</hibernate.version>
4041
<jsonpath>1.1.0</jsonpath>

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/alps/AlpsControllerIntegrationTests.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static class Config extends RepositoryRestConfigurerAdapter {
6969

7070
@Bean
7171
public LinkDiscoverer alpsLinkDiscoverer() {
72-
return new JsonPathLinkDiscoverer("$.descriptors[?(@.name == '%s')].href",
72+
return new JsonPathLinkDiscoverer("$.descriptor[?(@.name == '%s')].href",
7373
MediaType.valueOf("application/alps+json"));
7474
}
7575

@@ -101,7 +101,7 @@ public void exposesAlpsCollectionResources() throws Exception {
101101

102102
client.follow(peopleLink, RestMediaTypes.ALPS_JSON)//
103103
.andExpect(jsonPath("$.alps.version").value("1.0"))//
104-
.andExpect(jsonPath("$.alps.descriptors[*].name", hasItems("people", "person")));
104+
.andExpect(jsonPath("$.alps.descriptor[*].name", hasItems("people", "person")));
105105
}
106106

107107
@Test // DATAREST-638
@@ -112,7 +112,7 @@ public void verifyThatAlpsIsDefaultProfileFormat() throws Exception {
112112

113113
client.follow(peopleLink)//
114114
.andExpect(jsonPath("$.alps.version").value("1.0"))//
115-
.andExpect(jsonPath("$.alps.descriptors[*].name", hasItems("people", "person")));
115+
.andExpect(jsonPath("$.alps.descriptor[*].name", hasItems("people", "person")));
116116

117117
}
118118

@@ -124,11 +124,11 @@ public void verifyThatAttributesIgnoredDontAppearInAlps() throws Exception {
124124

125125
client.follow(itemsLink, RestMediaTypes.ALPS_JSON)//
126126
// Exposes standard property
127-
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", hasItems("name")))
127+
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", hasItems("name")))
128128
// Does not expose explicitly @JsonIgnored property
129-
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", not(hasItems("owner"))))
129+
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", not(hasItems("owner"))))
130130
// Does not expose properties pointing to non exposed types
131-
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", not(hasItems("manager", "curator"))));
131+
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", not(hasItems("manager", "curator"))));
132132
}
133133

134134
@Test // DATAREST-494
@@ -140,7 +140,7 @@ public void linksToJsonSchemaFromRepresentationDescriptor() throws Exception {
140140
assertThat(itemsLink).isNotNull();
141141

142142
String result = client.follow(itemsLink, RestMediaTypes.ALPS_JSON).andReturn().getResponse().getContentAsString();
143-
String href = JsonPath.<JSONArray> read(result, "$.alps.descriptors[?(@.id == 'item-representation')].href").get(0)
143+
String href = JsonPath.<JSONArray> read(result, "$.alps.descriptor[?(@.id == 'item-representation')].href").get(0)
144144
.toString();
145145

146146
assertThat(href, endsWith("/profile/items"));
@@ -153,8 +153,8 @@ public void referenceToAssociatedEntityDesciptorPointsToRepresentationDescriptor
153153
Link usersLink = client.discoverUnique(profileLink, "people", MediaType.ALL);
154154

155155
String jsonPath = "$.alps."; // Root
156-
jsonPath += "descriptors[?(@.id == 'person-representation')]."; // Representation descriptor
157-
jsonPath += "descriptors[?(@.name == 'father')]."; // First father descriptor
156+
jsonPath += "descriptor[?(@.id == 'person-representation')]."; // Representation descriptor
157+
jsonPath += "descriptor[?(@.name == 'father')]."; // First father descriptor
158158
jsonPath += "rt"; // Return type
159159

160160
String result = client.follow(usersLink, RestMediaTypes.ALPS_JSON).andReturn().getResponse().getContentAsString();
@@ -171,7 +171,7 @@ public void onlyExposesIdAttributesWhenExposedInTheConfiguration() throws Except
171171

172172
client.follow(itemsLink, RestMediaTypes.ALPS_JSON)//
173173
// Exposes identifier if configured to
174-
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", hasItems("id", "name")));
174+
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", hasItems("id", "name")));
175175
}
176176

177177
@Test // DATAREST-683
@@ -186,7 +186,7 @@ public void enumValueListingsAreTranslatedIfEnabled() throws Exception {
186186

187187
String value = JsonPath
188188
.<JSONArray> read(result,
189-
"$.alps.descriptors[?(@.id == 'person-representation')].descriptors[?(@.name == 'gender')].doc.value")
189+
"$.alps.descriptor[?(@.id == 'person-representation')].descriptor[?(@.name == 'gender')].doc.value")
190190
.get(0).toString();
191191

192192
assertThat(value).isEqualTo("Male, Female, Undefined");
@@ -202,7 +202,7 @@ public void alpsCanHandleGroovyDomainObjects() throws Exception {
202202

203203
String name = JsonPath
204204
.<JSONArray> read(result,
205-
"$.alps.descriptors[?(@.id == 'simulatedGroovyDomainClass-representation')].descriptors[0].name")
205+
"$.alps.descriptor[?(@.id == 'simulatedGroovyDomainClass-representation')].descriptor[0].name")
206206
.get(0).toString();
207207

208208
assertThat(name).isEqualTo("name");

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/DataRest262Tests.java

+24-8
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
import javax.validation.constraints.NotNull;
2727

2828
import org.junit.Before;
29-
import org.junit.Ignore;
3029
import org.junit.Test;
3130
import org.junit.runner.RunWith;
3231
import org.springframework.beans.factory.annotation.Autowired;
33-
import org.springframework.beans.factory.annotation.Qualifier;
3432
import org.springframework.context.ApplicationContext;
3533
import org.springframework.context.annotation.Configuration;
3634
import org.springframework.context.annotation.Import;
@@ -41,9 +39,16 @@
4139
import org.springframework.data.rest.webmvc.PersistentEntityResource;
4240
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
4341
import org.springframework.hateoas.Link;
42+
import org.springframework.hateoas.MediaTypes;
4443
import org.springframework.hateoas.Resource;
44+
import org.springframework.http.HttpHeaders;
45+
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
46+
import org.springframework.mock.web.MockHttpServletRequest;
4547
import org.springframework.test.context.ContextConfiguration;
4648
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
49+
import org.springframework.web.context.request.RequestAttributes;
50+
import org.springframework.web.context.request.RequestContextHolder;
51+
import org.springframework.web.context.request.ServletRequestAttributes;
4752

4853
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
4954
import com.fasterxml.jackson.annotation.PropertyAccessor;
@@ -69,11 +74,18 @@ static class Config {
6974
@Autowired ApplicationContext beanFactory;
7075
@Autowired JpaMetamodelMappingContext mappingContext;
7176
@Autowired AirportRepository repository;
72-
@Autowired @Qualifier("halObjectMapper") ObjectMapper mapper;
77+
78+
ObjectMapper mapper;
7379

7480
@Before
7581
public void setUp() {
76-
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
82+
83+
this.mapper = beanFactory //
84+
.getBean("halJacksonHttpMessageConverter", AbstractJackson2HttpMessageConverter.class) //
85+
.getObjectMapper() //
86+
.copy();
87+
88+
this.mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
7789
}
7890

7991
@Test // DATAREST-262
@@ -87,9 +99,13 @@ public void deserializesNestedAssociation() throws Exception {
8799
}
88100

89101
@Test // DATAREST-262
90-
@Ignore
91102
public void serializesLinksToNestedAssociations() throws Exception {
92103

104+
MockHttpServletRequest request = new MockHttpServletRequest();
105+
request.addHeader(HttpHeaders.ACCEPT, MediaTypes.HAL_JSON_VALUE);
106+
RequestAttributes attributes = new ServletRequestAttributes(request);
107+
RequestContextHolder.setRequestAttributes(attributes);
108+
93109
Airport first = new Airport();
94110
first.id = 1L;
95111

@@ -112,9 +128,9 @@ public void serializesLinksToNestedAssociations() throws Exception {
112128

113129
String result = mapper.writeValueAsString(resource);
114130

115-
assertThat(JsonPath.<Object> read(result, "$_links.self")).isNotNull();
116-
assertThat(JsonPath.<Object> read(result, "$_links.airport")).isNotNull();
117-
assertThat(JsonPath.<Object> read(result, "$_links.originOrDestinationAirport")).isNotNull();
131+
assertThat(JsonPath.<Object> read(result, "$._links.self")).isNotNull();
132+
assertThat(JsonPath.<Object> read(result, "$._links.originOrDestinationAirport")).isNotNull();
133+
assertThat(JsonPath.<Object> read(result, "$.orgOrDstFlightPart._links.airport")).isNotNull();
118134
}
119135

120136
public interface AircraftMovementRepository extends CrudRepository<AircraftMovement, Long> {

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/json/Jackson2DatatypeHelperIntegrationTests.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.Test;
2424
import org.junit.runner.RunWith;
2525
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.context.ApplicationContext;
2627
import org.springframework.data.mapping.PersistentEntity;
2728
import org.springframework.data.mapping.PersistentProperty;
2829
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -33,6 +34,7 @@
3334
import org.springframework.data.rest.webmvc.jpa.OrderRepository;
3435
import org.springframework.data.rest.webmvc.jpa.Person;
3536
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
37+
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
3638
import org.springframework.test.context.ContextConfiguration;
3739
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
3840
import org.springframework.transaction.annotation.Transactional;
@@ -50,18 +52,22 @@
5052
public class Jackson2DatatypeHelperIntegrationTests {
5153

5254
@Autowired PersistentEntities entities;
53-
@Autowired ObjectMapper objectMapper;
55+
@Autowired ApplicationContext context;
5456

5557
@Autowired PersonRepository people;
5658
@Autowired OrderRepository orders;
5759
@Autowired EntityManager em;
5860

61+
ObjectMapper objectMapper;
62+
5963
Order order;
6064

6165
@Before
6266
public void setUp() {
6367

6468
this.order = orders.save(new Order(people.save(new Person("Dave", "Matthews"))));
69+
this.objectMapper = context.getBean("halJacksonHttpMessageConverter", AbstractJackson2HttpMessageConverter.class)
70+
.getObjectMapper();
6571

6672
// Reset JPA to make sure the query returns a result with proxy references
6773
em.flush();

spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssemblerIntegrationTests.java

-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.junit.Test;
2626
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
2727
import org.springframework.beans.factory.annotation.Autowired;
28-
import org.springframework.beans.factory.annotation.Qualifier;
2928
import org.springframework.data.mapping.context.PersistentEntities;
3029
import org.springframework.data.rest.core.support.DefaultSelfLinkProvider;
3130
import org.springframework.data.rest.core.support.EntityLookup;
@@ -39,8 +38,6 @@
3938
import org.springframework.hateoas.Links;
4039
import org.springframework.test.context.ContextConfiguration;
4140

42-
import com.fasterxml.jackson.databind.ObjectMapper;
43-
4441
/**
4542
* Integration tests for {@link PersistentEntityResourceAssembler}.
4643
*
@@ -51,7 +48,6 @@ public class PersistentEntityResourceAssemblerIntegrationTests extends AbstractC
5148

5249
@Autowired PersistentEntities entities;
5350
@Autowired EntityLinks entityLinks;
54-
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
5551
@Autowired Associations associations;
5652

5753
@Test // DATAREST-609

spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverterUnitTests.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import org.springframework.data.rest.tests.mongodb.User.EmailAddress;
4343
import org.springframework.data.rest.tests.mongodb.User.TypeWithPattern;
4444
import org.springframework.data.rest.tests.mongodb.groovy.SimulatedGroovyDomainClass;
45-
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
45+
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
4646
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
4747
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter.ValueTypeSchemaPropertyCustomizerFactory;
4848
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverterUnitTests.TestConfiguration;
@@ -66,12 +66,13 @@ public class PersistentEntityToJsonSchemaConverterUnitTests {
6666
@Autowired @Qualifier("resourceDescriptionMessageSourceAccessor") MessageSourceAccessor accessor;
6767
@Autowired RepositoryRestConfiguration configuration;
6868
@Autowired PersistentEntities entities;
69-
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
7069
@Autowired Associations associations;
7170

71+
ObjectMapper objectMapper = new ObjectMapper();
72+
7273
@Configuration
7374
@Import(RepositoryRestMvcConfiguration.class)
74-
static class TestConfiguration extends RepositoryRestConfigurerAdapter {
75+
static class TestConfiguration implements RepositoryRestConfigurer {
7576

7677
@Override
7778
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java

+30-17
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@
2222
import java.util.HashSet;
2323
import java.util.List;
2424
import java.util.Map;
25+
import java.util.Optional;
2526
import java.util.Set;
2627

2728
import org.springframework.beans.factory.BeanClassLoaderAware;
2829
import org.springframework.beans.factory.BeanCreationException;
2930
import org.springframework.beans.factory.BeanFactoryUtils;
3031
import org.springframework.beans.factory.InitializingBean;
3132
import org.springframework.beans.factory.ObjectFactory;
33+
import org.springframework.beans.factory.ObjectProvider;
3234
import org.springframework.beans.factory.annotation.Autowired;
3335
import org.springframework.beans.factory.annotation.Qualifier;
3436
import org.springframework.beans.factory.config.PropertiesFactoryBean;
@@ -117,6 +119,7 @@
117119
import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables;
118120
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
119121
import org.springframework.data.util.AnnotatedTypeScanner;
122+
import org.springframework.data.util.Lazy;
120123
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
121124
import org.springframework.data.web.HateoasSortHandlerMethodArgumentResolver;
122125
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
@@ -132,6 +135,7 @@
132135
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
133136
import org.springframework.hateoas.core.EvoInflectorRelProvider;
134137
import org.springframework.hateoas.hal.CurieProvider;
138+
import org.springframework.hateoas.hal.HalConfiguration;
135139
import org.springframework.hateoas.hal.Jackson2HalModule;
136140
import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator;
137141
import org.springframework.hateoas.mvc.ResourceProcessorInvoker;
@@ -189,15 +193,32 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
189193
@Autowired(required = false) List<RepositoryRestConfigurer> configurers = Collections.emptyList();
190194
@Autowired(required = false) List<EntityLookup<?>> lookups = Collections.emptyList();
191195

192-
@Autowired(required = false) RelProvider relProvider;
193-
@Autowired(required = false) CurieProvider curieProvider;
196+
@Autowired Optional<RelProvider> relProvider;
197+
@Autowired Optional<CurieProvider> curieProvider;
198+
@Autowired Optional<HalConfiguration> halConfiguration;
199+
@Autowired ObjectProvider<ObjectMapper> objectMapper;
200+
201+
private final Lazy<ObjectMapper> mapper;
194202

195203
private RepositoryRestConfigurerDelegate configurerDelegate;
196204
private ClassLoader beanClassLoader;
197205

198206
public RepositoryRestMvcConfiguration(ApplicationContext context,
199207
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
208+
200209
super(context, conversionService);
210+
211+
this.mapper = Lazy.of(() -> {
212+
213+
Jdk8Module jdk8Module = new Jdk8Module();
214+
jdk8Module.configureAbsentsAsNulls(true);
215+
216+
ObjectMapper mapper = basicObjectMapper();
217+
mapper.registerModule(persistentEntityJackson2Module());
218+
mapper.registerModule(jdk8Module);
219+
220+
return mapper;
221+
});
201222
}
202223

203224
/*
@@ -461,17 +482,8 @@ public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() {
461482
*
462483
* @return
463484
*/
464-
@Bean
465485
public ObjectMapper objectMapper() {
466-
467-
Jdk8Module jdk8Module = new Jdk8Module();
468-
jdk8Module.configureAbsentsAsNulls(true);
469-
470-
ObjectMapper mapper = basicObjectMapper();
471-
mapper.registerModule(persistentEntityJackson2Module());
472-
mapper.registerModule(jdk8Module);
473-
474-
return mapper;
486+
return mapper.get();
475487
}
476488

477489
/**
@@ -529,13 +541,13 @@ public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageC
529541
return converter;
530542
}
531543

532-
@Bean
533544
public ObjectMapper halObjectMapper() {
534545

535-
RelProvider defaultedRelProvider = this.relProvider != null ? this.relProvider : new EvoInflectorRelProvider();
546+
RelProvider defaultedRelProvider = this.relProvider.orElseGet(() -> new EvoInflectorRelProvider());
547+
HalConfiguration halConfiguration = this.halConfiguration.orElseGet(() -> new HalConfiguration());
536548

537-
HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider, curieProvider,
538-
resourceDescriptionMessageSourceAccessor(), applicationContext.getAutowireCapableBeanFactory());
549+
HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider, curieProvider.orElse(null),
550+
resourceDescriptionMessageSourceAccessor(), halConfiguration);
539551

540552
ObjectMapper mapper = basicObjectMapper();
541553
mapper.registerModule(persistentEntityJackson2Module());
@@ -849,7 +861,8 @@ protected List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {
849861

850862
protected ObjectMapper basicObjectMapper() {
851863

852-
ObjectMapper objectMapper = new ObjectMapper();
864+
ObjectMapper mapper = this.objectMapper.getIfAvailable();
865+
ObjectMapper objectMapper = mapper == null ? new ObjectMapper() : mapper.copy();
853866

854867
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
855868
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

0 commit comments

Comments
 (0)