Skip to content

Commit 8cfbfc1

Browse files
committed
#719 - Simplified configuration setup.
We now avoid to register ObjectMapper instances as Spring beans and rather use one already existing in the ApplicationContext and copying the setup before registering the individual HttpMessageConverters for the individual media types. Replaced a lot of the programmatic component setup via BeanDefinitions with their corresponding JavaConfig alternatives. Removed obsolete media type specific HttpMessageConverters and configuration classes registering them.
1 parent 900b2e5 commit 8cfbfc1

15 files changed

+528
-838
lines changed

src/main/java/org/springframework/hateoas/collectionjson/CollectionJsonMessageConverter.java

Lines changed: 0 additions & 83 deletions
This file was deleted.

src/main/java/org/springframework/hateoas/collectionjson/CollectionJsonWebMvcConfigurer.java

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2018 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.hateoas.config;
17+
18+
import lombok.RequiredArgsConstructor;
19+
20+
import org.springframework.beans.BeansException;
21+
import org.springframework.beans.factory.ObjectFactory;
22+
import org.springframework.beans.factory.config.BeanPostProcessor;
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.hateoas.hal.Jackson2HalModule;
25+
import org.springframework.web.client.RestTemplate;
26+
27+
import com.fasterxml.jackson.databind.ObjectMapper;
28+
29+
/**
30+
* {@link BeanPostProcessor} to register {@link Jackson2HalModule} with {@link ObjectMapper} instances registered in the
31+
* {@link ApplicationContext}.
32+
*
33+
* @author Oliver Gierke
34+
*/
35+
@RequiredArgsConstructor
36+
class ConverterRegisteringBeanPostProcessor implements BeanPostProcessor {
37+
38+
private final ObjectFactory<ConverterRegisteringWebMvcConfigurer> configurer;
39+
40+
/*
41+
* (non-Javadoc)
42+
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
43+
*/
44+
@Override
45+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
46+
47+
if (bean instanceof RestTemplate) {
48+
49+
ConverterRegisteringWebMvcConfigurer object = configurer.getObject();
50+
object.extendMessageConverters(((RestTemplate) bean).getMessageConverters());
51+
}
52+
53+
return bean;
54+
}
55+
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Copyright 2018 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.hateoas.config;
17+
18+
import static org.springframework.hateoas.MediaTypes.*;
19+
20+
import lombok.RequiredArgsConstructor;
21+
22+
import java.util.Arrays;
23+
import java.util.Collection;
24+
import java.util.List;
25+
26+
import org.springframework.beans.BeansException;
27+
import org.springframework.beans.factory.BeanFactory;
28+
import org.springframework.beans.factory.BeanFactoryAware;
29+
import org.springframework.beans.factory.ObjectProvider;
30+
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.context.support.MessageSourceAccessor;
32+
import org.springframework.hateoas.RelProvider;
33+
import org.springframework.hateoas.ResourceSupport;
34+
import org.springframework.hateoas.collectionjson.Jackson2CollectionJsonModule;
35+
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
36+
import org.springframework.hateoas.core.DelegatingRelProvider;
37+
import org.springframework.hateoas.hal.CurieProvider;
38+
import org.springframework.hateoas.hal.HalConfiguration;
39+
import org.springframework.hateoas.hal.Jackson2HalModule;
40+
import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator;
41+
import org.springframework.hateoas.hal.forms.HalFormsConfiguration;
42+
import org.springframework.hateoas.hal.forms.Jackson2HalFormsModule;
43+
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
44+
import org.springframework.http.converter.HttpMessageConverter;
45+
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
46+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
47+
48+
import com.fasterxml.jackson.databind.DeserializationFeature;
49+
import com.fasterxml.jackson.databind.ObjectMapper;
50+
51+
/**
52+
* @author Oliver Gierke
53+
*/
54+
@Configuration
55+
@RequiredArgsConstructor
56+
public class ConverterRegisteringWebMvcConfigurer implements WebMvcConfigurer, BeanFactoryAware {
57+
58+
private static final String MESSAGE_SOURCE_BEAN_NAME = "linkRelationMessageSource";
59+
60+
private final ObjectProvider<ObjectMapper> mapper;
61+
private final ObjectProvider<DelegatingRelProvider> relProvider;
62+
private final ObjectProvider<CurieProvider> curieProvider;
63+
private final ObjectProvider<HalConfiguration> halConfiguration;
64+
private final ObjectProvider<HalFormsConfiguration> halFormsConfiguration;
65+
66+
private BeanFactory beanFactory;
67+
private Collection<HypermediaType> hypermediaTypes;
68+
69+
/*
70+
* (non-Javadoc)
71+
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
72+
*/
73+
@Override
74+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
75+
this.beanFactory = beanFactory;
76+
}
77+
78+
/**
79+
* @param hyperMediaTypes the hyperMediaTypes to set
80+
*/
81+
public void setHypermediaTypes(Collection<HypermediaType> hyperMediaTypes) {
82+
this.hypermediaTypes = hyperMediaTypes;
83+
}
84+
85+
/*
86+
* (non-Javadoc)
87+
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#extendMessageConverters(java.util.List)
88+
*/
89+
@Override
90+
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
91+
92+
for (HttpMessageConverter<?> converter : converters) {
93+
if (converter instanceof MappingJackson2HttpMessageConverter) {
94+
MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
95+
ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
96+
if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
97+
return;
98+
}
99+
}
100+
}
101+
102+
ObjectMapper objectMapper = mapper.getIfAvailable(() -> new ObjectMapper());
103+
104+
CurieProvider curieProvider = this.curieProvider.getIfAvailable();
105+
RelProvider relProvider = this.relProvider.getObject();
106+
107+
MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME,
108+
MessageSourceAccessor.class);
109+
110+
if (hypermediaTypes.contains(HypermediaType.HAL)) {
111+
converters.add(0, createHalConverter(objectMapper, curieProvider, relProvider, linkRelationMessageSource));
112+
}
113+
114+
if (hypermediaTypes.contains(HypermediaType.HAL_FORMS)) {
115+
converters.add(0, createHalFormsConverter(objectMapper, curieProvider, relProvider, linkRelationMessageSource));
116+
}
117+
118+
if (hypermediaTypes.contains(HypermediaType.COLLECTION_JSON)) {
119+
converters.add(0, createCollectionJsonConverter(objectMapper, linkRelationMessageSource));
120+
}
121+
}
122+
123+
/**
124+
* @param objectMapper
125+
* @param linkRelationMessageSource
126+
* @return
127+
*/
128+
protected MappingJackson2HttpMessageConverter createCollectionJsonConverter(ObjectMapper objectMapper,
129+
MessageSourceAccessor linkRelationMessageSource) {
130+
ObjectMapper collectionJsonObjectMapper = objectMapper.copy();
131+
132+
collectionJsonObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
133+
collectionJsonObjectMapper.registerModule(new Jackson2CollectionJsonModule());
134+
collectionJsonObjectMapper.setHandlerInstantiator(
135+
new Jackson2CollectionJsonModule.CollectionJsonHandlerInstantiator(linkRelationMessageSource));
136+
137+
MappingJackson2HttpMessageConverter collectionJsonConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
138+
ResourceSupport.class);
139+
collectionJsonConverter.setSupportedMediaTypes(Arrays.asList(COLLECTION_JSON));
140+
collectionJsonConverter.setObjectMapper(collectionJsonObjectMapper);
141+
return collectionJsonConverter;
142+
}
143+
144+
/**
145+
* @param objectMapper
146+
* @param curieProvider
147+
* @param relProvider
148+
* @param linkRelationMessageSource
149+
* @return
150+
*/
151+
private MappingJackson2HttpMessageConverter createHalFormsConverter(ObjectMapper objectMapper,
152+
CurieProvider curieProvider, RelProvider relProvider, MessageSourceAccessor linkRelationMessageSource) {
153+
154+
Jackson2HalFormsModule.HalFormsHandlerInstantiator hi = new Jackson2HalFormsModule.HalFormsHandlerInstantiator(
155+
relProvider, curieProvider, linkRelationMessageSource, true,
156+
this.halFormsConfiguration.getIfAvailable(() -> new HalFormsConfiguration()));
157+
ObjectMapper mapper = objectMapper.copy();
158+
159+
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
160+
mapper.registerModule(new Jackson2HalFormsModule());
161+
mapper.setHandlerInstantiator(hi);
162+
163+
MappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
164+
ResourceSupport.class);
165+
converter.setSupportedMediaTypes(Arrays.asList(HAL_FORMS_JSON));
166+
converter.setObjectMapper(mapper);
167+
168+
return converter;
169+
}
170+
171+
/**
172+
* @param objectMapper
173+
* @param curieProvider
174+
* @param relProvider
175+
* @param linkRelationMessageSource
176+
* @return
177+
*/
178+
private MappingJackson2HttpMessageConverter createHalConverter(ObjectMapper objectMapper, CurieProvider curieProvider,
179+
RelProvider relProvider, MessageSourceAccessor linkRelationMessageSource) {
180+
181+
HalConfiguration halConfiguration = this.halConfiguration.getIfAvailable(() -> new HalConfiguration());
182+
183+
HalHandlerInstantiator instantiator = new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider,
184+
linkRelationMessageSource, halConfiguration);
185+
186+
ObjectMapper mapper = objectMapper.copy();
187+
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
188+
mapper.registerModule(new Jackson2HalModule());
189+
mapper.setHandlerInstantiator(instantiator);
190+
191+
MappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
192+
ResourceSupport.class);
193+
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
194+
converter.setObjectMapper(mapper);
195+
196+
return converter;
197+
}
198+
}

src/main/java/org/springframework/hateoas/config/EnableEntityLinks.java

Lines changed: 2 additions & 3 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-2018 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.
@@ -36,7 +36,6 @@
3636
@Target(ElementType.TYPE)
3737
@Inherited
3838
@Documented
39-
@Import(LinkBuilderBeanDefinitionRegistrar.class)
39+
@Import(EntityLinksConfiguration.class)
4040
public @interface EnableEntityLinks {
41-
4241
}

0 commit comments

Comments
 (0)