#723 - 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. Backport of fix for #719.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018 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.hateoas.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to register {@link Jackson2HalModule} with {@link ObjectMapper} instances registered in the
|
||||
* {@link ApplicationContext}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class ConverterRegisteringBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectFactory<ConverterRegisteringWebMvcConfigurer> configurer;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (bean instanceof RestTemplate) {
|
||||
|
||||
ConverterRegisteringWebMvcConfigurer object = configurer.getObject();
|
||||
object.extendMessageConverters(((RestTemplate) bean).getMessageConverters());
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2018 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.hateoas.config;
|
||||
|
||||
import static org.springframework.hateoas.MediaTypes.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.core.DelegatingRelProvider;
|
||||
import org.springframework.hateoas.hal.CurieProvider;
|
||||
import org.springframework.hateoas.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator;
|
||||
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ConverterRegisteringWebMvcConfigurer extends WebMvcConfigurerAdapter implements BeanFactoryAware {
|
||||
|
||||
private static final String MESSAGE_SOURCE_BEAN_NAME = "linkRelationMessageSource";
|
||||
|
||||
private final ObjectProvider<ObjectMapper> mapper;
|
||||
private final ObjectProvider<DelegatingRelProvider> relProvider;
|
||||
private final ObjectProvider<CurieProvider> curieProvider;
|
||||
private final ObjectProvider<HalConfiguration> halConfiguration;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
private Collection<HypermediaType> hypermediaTypes;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hyperMediaTypes the hyperMediaTypes to set
|
||||
*/
|
||||
public void setHypermediaTypes(Collection<HypermediaType> hyperMediaTypes) {
|
||||
this.hypermediaTypes = hyperMediaTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#extendMessageConverters(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
|
||||
for (HttpMessageConverter<?> converter : converters) {
|
||||
if (converter instanceof MappingJackson2HttpMessageConverter) {
|
||||
MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
|
||||
ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
|
||||
if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectMapper objectMapper = mapper.getIfAvailable();
|
||||
objectMapper = objectMapper == null ? new ObjectMapper() : objectMapper;
|
||||
|
||||
CurieProvider curieProvider = this.curieProvider.getIfAvailable();
|
||||
RelProvider relProvider = this.relProvider.getObject();
|
||||
MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME,
|
||||
MessageSourceAccessor.class);
|
||||
|
||||
if (hypermediaTypes.contains(HypermediaType.HAL)) {
|
||||
converters.add(0, createHalConverter(objectMapper, curieProvider, relProvider, linkRelationMessageSource));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param objectMapper
|
||||
* @param curieProvider
|
||||
* @param relProvider
|
||||
* @param linkRelationMessageSource
|
||||
* @return
|
||||
*/
|
||||
private MappingJackson2HttpMessageConverter createHalConverter(ObjectMapper objectMapper, CurieProvider curieProvider,
|
||||
RelProvider relProvider, MessageSourceAccessor linkRelationMessageSource) {
|
||||
|
||||
HalConfiguration halConfiguration = this.halConfiguration.getIfAvailable();
|
||||
halConfiguration = halConfiguration == null ? new HalConfiguration() : halConfiguration;
|
||||
|
||||
HalHandlerInstantiator instantiator = new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider,
|
||||
linkRelationMessageSource, halConfiguration);
|
||||
|
||||
ObjectMapper mapper = objectMapper.copy();
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
mapper.registerModule(new Jackson2HalModule());
|
||||
mapper.setHandlerInstantiator(instantiator);
|
||||
|
||||
MappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
|
||||
ResourceSupport.class);
|
||||
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
|
||||
converter.setObjectMapper(mapper);
|
||||
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -36,7 +36,9 @@ import org.springframework.hateoas.core.DelegatingEntityLinks;
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import(LinkBuilderBeanDefinitionRegistrar.class)
|
||||
@Import({ //
|
||||
EntityLinksConfiguration.class, //
|
||||
JaxRsConfigurationImportSelector.class //
|
||||
})
|
||||
public @interface EnableEntityLinks {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -39,11 +39,16 @@ import org.springframework.hateoas.LinkDiscoverer;
|
||||
* @see LinkDiscoverer
|
||||
* @see EntityLinks
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class })
|
||||
@EnableEntityLinks
|
||||
@Import({ //
|
||||
HypermediaSupportBeanDefinitionRegistrar.class, //
|
||||
HateoasConfiguration.class //
|
||||
})
|
||||
public @interface EnableHypermediaSupport {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2018 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.hateoas.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.core.ControllerEntityLinksFactoryBean;
|
||||
import org.springframework.hateoas.core.DelegatingEntityLinks;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilderFactory;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
/**
|
||||
* Spring configuration to register a {@link PluginRegistry} for {@link EntityLinks}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
class EntityLinksConfiguration {
|
||||
|
||||
@Bean
|
||||
PluginRegistryFactoryBean<EntityLinks, Class<?>> entityLinksPluginRegistry() {
|
||||
|
||||
PluginRegistryFactoryBean<EntityLinks, Class<?>> registry = new PluginRegistryFactoryBean<EntityLinks, Class<?>>();
|
||||
registry.setType(EntityLinks.class);
|
||||
registry.setExclusions(new Class[] { DelegatingEntityLinks.class });
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
@DependsOn("controllerEntityLinks")
|
||||
DelegatingEntityLinks delegatingEntityLinks(PluginRegistry<EntityLinks, Class<?>> entityLinksPluginRegistry) {
|
||||
return new DelegatingEntityLinks(entityLinksPluginRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ControllerEntityLinksFactoryBean controllerEntityLinks(ControllerLinkBuilderFactory controllerLinkBuilderFactory) {
|
||||
|
||||
ControllerEntityLinksFactoryBean factory = new ControllerEntityLinksFactoryBean();
|
||||
factory.setAnnotation(Controller.class);
|
||||
factory.setLinkBuilderFactory(controllerLinkBuilderFactory);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ControllerLinkBuilderFactory controllerLinkBuilderFactoryBean() {
|
||||
return new ControllerLinkBuilderFactory();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2018 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.
|
||||
@@ -16,10 +16,24 @@
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.LinkDiscoverers;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.core.AnnotationRelProvider;
|
||||
import org.springframework.hateoas.core.DefaultRelProvider;
|
||||
import org.springframework.hateoas.core.DelegatingRelProvider;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.plugin.core.config.EnablePluginRegistries;
|
||||
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Common HATEOAS specific configuration.
|
||||
@@ -29,6 +43,7 @@ import org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
* @since 0.19
|
||||
*/
|
||||
@Configuration
|
||||
@EnablePluginRegistries({ LinkDiscoverer.class })
|
||||
class HateoasConfiguration {
|
||||
|
||||
/**
|
||||
@@ -50,4 +65,49 @@ class HateoasConfiguration {
|
||||
throw new BeanCreationException("resourceDescriptionMessageSourceAccessor", "", o_O);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
ConverterRegisteringBeanPostProcessor jackson2ModuleRegisteringBeanPostProcessor(
|
||||
ObjectFactory<ConverterRegisteringWebMvcConfigurer> configurer) {
|
||||
return new ConverterRegisteringBeanPostProcessor(configurer);
|
||||
}
|
||||
|
||||
// RelProvider
|
||||
|
||||
@Bean
|
||||
RelProvider defaultRelProvider() {
|
||||
|
||||
return ClassUtils.isPresent("org.atteo.evo.inflector.English", null) //
|
||||
? new EvoInflectorRelProvider()
|
||||
: new DefaultRelProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
AnnotationRelProvider annotationRelProvider() {
|
||||
return new AnnotationRelProvider();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
DelegatingRelProvider _relProvider(PluginRegistry<RelProvider, Class<?>> relProviderPluginRegistry) {
|
||||
return new DelegatingRelProvider(relProviderPluginRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PluginRegistryFactoryBean<RelProvider, Class<?>> relProviderPluginRegistry() {
|
||||
|
||||
PluginRegistryFactoryBean<RelProvider, Class<?>> factory = new PluginRegistryFactoryBean<RelProvider, Class<?>>();
|
||||
|
||||
factory.setType(RelProvider.class);
|
||||
factory.setExclusions(new Class[] { DelegatingRelProvider.class });
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
// LinkDiscoverers
|
||||
|
||||
@Bean
|
||||
LinkDiscoverers linkDiscoverers(PluginRegistry<LinkDiscoverer, MediaType> linkDiscovererRegistry) {
|
||||
return new LinkDiscoverers(linkDiscovererRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,59 +17,25 @@ package org.springframework.hateoas.config;
|
||||
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.*;
|
||||
import static org.springframework.hateoas.MediaTypes.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.LinkDiscoverers;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.core.AnnotationRelProvider;
|
||||
import org.springframework.hateoas.core.DefaultRelProvider;
|
||||
import org.springframework.hateoas.core.DelegatingRelProvider;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
import org.springframework.hateoas.hal.CurieProvider;
|
||||
import org.springframework.hateoas.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.hal.HalLinkDiscoverer;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link ImportBeanDefinitionRegistrar} implementation to activate hypermedia support based on the configured
|
||||
@@ -77,22 +43,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* activated as well).
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
|
||||
class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
private static final String DELEGATING_REL_PROVIDER_BEAN_NAME = "_relProvider";
|
||||
private static final String LINK_DISCOVERER_REGISTRY_BEAN_NAME = "_linkDiscovererRegistry";
|
||||
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";
|
||||
private static final String MESSAGE_SOURCE_BEAN_NAME = "linkRelationMessageSource";
|
||||
|
||||
private static final boolean JACKSON2_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
null);
|
||||
private static final boolean JSONPATH_PRESENT = ClassUtils.isPresent("com.jayway.jsonpath.JsonPath", null);
|
||||
private static final boolean EVO_PRESENT = ClassUtils.isPresent("org.atteo.evo.inflector.English", null);
|
||||
|
||||
private final ImportBeanDefinitionRegistrar linkBuilderBeanDefinitionRegistrar = new LinkBuilderBeanDefinitionRegistrar();
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -101,14 +56,12 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
linkBuilderBeanDefinitionRegistrar.registerBeanDefinitions(metadata, registry);
|
||||
|
||||
Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableHypermediaSupport.class.getName());
|
||||
Collection<HypermediaType> types = Arrays.asList((HypermediaType[]) attributes.get("type"));
|
||||
|
||||
for (HypermediaType type : types) {
|
||||
if (JSONPATH_PRESENT) {
|
||||
|
||||
if (JSONPATH_PRESENT) {
|
||||
for (HypermediaType type : types) {
|
||||
|
||||
AbstractBeanDefinition linkDiscovererBeanDefinition = getLinkDiscovererBeanDefinition(type);
|
||||
registerBeanDefinition(new BeanDefinitionHolder(linkDiscovererBeanDefinition,
|
||||
@@ -116,76 +69,9 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
|
||||
}
|
||||
}
|
||||
|
||||
if (types.contains(HypermediaType.HAL)) {
|
||||
|
||||
if (JACKSON2_PRESENT) {
|
||||
|
||||
BeanDefinitionBuilder halQueryMapperBuilder = rootBeanDefinition(ObjectMapper.class);
|
||||
registerSourcedBeanDefinition(halQueryMapperBuilder, metadata, registry, HAL_OBJECT_MAPPER_BEAN_NAME);
|
||||
|
||||
BeanDefinitionBuilder customizerBeanDefinition = rootBeanDefinition(DefaultObjectMapperCustomizer.class);
|
||||
registerSourcedBeanDefinition(customizerBeanDefinition, metadata, registry);
|
||||
|
||||
BeanDefinitionBuilder builder = rootBeanDefinition(Jackson2ModuleRegisteringBeanPostProcessor.class);
|
||||
registerSourcedBeanDefinition(builder, metadata, registry);
|
||||
}
|
||||
|
||||
// If no HalConfiguration bean, create a default one.
|
||||
if (this.beanFactory.getBeanNamesForType(HalConfiguration.class).length == 0) {
|
||||
registerSourcedBeanDefinition(rootBeanDefinition(HalConfiguration.class), metadata, registry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!types.isEmpty()) {
|
||||
|
||||
BeanDefinitionBuilder linkDiscoverersRegistryBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PluginRegistryFactoryBean.class);
|
||||
linkDiscoverersRegistryBuilder.addPropertyValue("type", LinkDiscoverer.class);
|
||||
registerSourcedBeanDefinition(linkDiscoverersRegistryBuilder, metadata, registry,
|
||||
LINK_DISCOVERER_REGISTRY_BEAN_NAME);
|
||||
|
||||
BeanDefinitionBuilder linkDiscoverersBuilder = BeanDefinitionBuilder.rootBeanDefinition(LinkDiscoverers.class);
|
||||
linkDiscoverersBuilder.addConstructorArgReference(LINK_DISCOVERER_REGISTRY_BEAN_NAME);
|
||||
registerSourcedBeanDefinition(linkDiscoverersBuilder, metadata, registry);
|
||||
}
|
||||
|
||||
registerRelProviderPluginRegistryAndDelegate(registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (ListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers bean definitions for a {@link PluginRegistry} to capture {@link RelProvider} instances. Wraps the
|
||||
* registry into a {@link DelegatingRelProvider} bean definition backed by the registry.
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
private static void registerRelProviderPluginRegistryAndDelegate(BeanDefinitionRegistry registry) {
|
||||
|
||||
Class<?> defaultRelProviderType = EVO_PRESENT ? EvoInflectorRelProvider.class : DefaultRelProvider.class;
|
||||
RootBeanDefinition defaultRelProviderBeanDefinition = new RootBeanDefinition(defaultRelProviderType);
|
||||
registry.registerBeanDefinition("defaultRelProvider", defaultRelProviderBeanDefinition);
|
||||
|
||||
RootBeanDefinition annotationRelProviderBeanDefinition = new RootBeanDefinition(AnnotationRelProvider.class);
|
||||
registry.registerBeanDefinition("annotationRelProvider", annotationRelProviderBeanDefinition);
|
||||
|
||||
BeanDefinitionBuilder registryFactoryBeanBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PluginRegistryFactoryBean.class);
|
||||
registryFactoryBeanBuilder.addPropertyValue("type", RelProvider.class);
|
||||
registryFactoryBeanBuilder.addPropertyValue("exclusions", DelegatingRelProvider.class);
|
||||
|
||||
AbstractBeanDefinition registryBeanDefinition = registryFactoryBeanBuilder.getBeanDefinition();
|
||||
registry.registerBeanDefinition("relProviderPluginRegistry", registryBeanDefinition);
|
||||
|
||||
BeanDefinitionBuilder delegateBuilder = BeanDefinitionBuilder.rootBeanDefinition(DelegatingRelProvider.class);
|
||||
delegateBuilder.addConstructorArgValue(registryBeanDefinition);
|
||||
|
||||
AbstractBeanDefinition beanDefinition = delegateBuilder.getBeanDefinition();
|
||||
beanDefinition.setPrimary(true);
|
||||
registry.registerBeanDefinition(DELEGATING_REL_PROVIDER_BEAN_NAME, beanDefinition);
|
||||
BeanDefinitionBuilder configurerBeanDefinition = rootBeanDefinition(ConverterRegisteringWebMvcConfigurer.class);
|
||||
configurerBeanDefinition.addPropertyValue("hypermediaTypes", types);
|
||||
registerSourcedBeanDefinition(configurerBeanDefinition, metadata, registry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,136 +114,4 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
|
||||
registerBeanDefinition(holder, registry);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to register {@link Jackson2HalModule} with {@link ObjectMapper} instances registered in
|
||||
* the {@link ApplicationContext}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class Jackson2ModuleRegisteringBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
|
||||
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
Assert.isInstanceOf(AutowireCapableBeanFactory.class, beanFactory,
|
||||
"BeanFactory must be an AutowireCapableBeanFactory!");
|
||||
|
||||
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (bean instanceof RequestMappingHandlerAdapter) {
|
||||
|
||||
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
|
||||
adapter.setMessageConverters(potentiallyRegisterModule(adapter.getMessageConverters()));
|
||||
}
|
||||
|
||||
if (bean instanceof RestTemplate) {
|
||||
|
||||
RestTemplate template = (RestTemplate) bean;
|
||||
template.setMessageConverters(potentiallyRegisterModule(template.getMessageConverters()));
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private List<HttpMessageConverter<?>> potentiallyRegisterModule(List<HttpMessageConverter<?>> converters) {
|
||||
|
||||
for (HttpMessageConverter<?> converter : converters) {
|
||||
if (converter instanceof MappingJackson2HttpMessageConverter) {
|
||||
MappingJackson2HttpMessageConverter halConverterCandidate = (MappingJackson2HttpMessageConverter) converter;
|
||||
ObjectMapper objectMapper = halConverterCandidate.getObjectMapper();
|
||||
if (Jackson2HalModule.isAlreadyRegisteredIn(objectMapper)) {
|
||||
return converters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurieProvider curieProvider = getCurieProvider(beanFactory);
|
||||
RelProvider relProvider = beanFactory.getBean(DELEGATING_REL_PROVIDER_BEAN_NAME, RelProvider.class);
|
||||
ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);
|
||||
MessageSourceAccessor linkRelationMessageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME,
|
||||
MessageSourceAccessor.class);
|
||||
|
||||
halObjectMapper.registerModule(new Jackson2HalModule());
|
||||
halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider,
|
||||
linkRelationMessageSource, beanFactory));
|
||||
|
||||
MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
|
||||
ResourceSupport.class);
|
||||
halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON, HAL_JSON_UTF8));
|
||||
halConverter.setObjectMapper(halObjectMapper);
|
||||
|
||||
List<HttpMessageConverter<?>> result = new ArrayList<HttpMessageConverter<?>>(converters.size());
|
||||
result.add(halConverter);
|
||||
result.addAll(converters);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static CurieProvider getCurieProvider(BeanFactory factory) {
|
||||
|
||||
try {
|
||||
return factory.getBean(CurieProvider.class);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to disable the default HAL {@link ObjectMapper} to fail on unknown properties. Needed as
|
||||
* the methods to do that on {@link Jackson2ObjectMapperFactoryBean} were introduced in Spring 4.1 only.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class DefaultObjectMapperCustomizer implements BeanPostProcessor {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (!HAL_OBJECT_MAPPER_BEAN_NAME.equals(beanName)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
ObjectMapper mapper = (ObjectMapper) bean;
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2018 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.hateoas.config;
|
||||
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.hateoas.core.ControllerEntityLinksFactoryBean;
|
||||
import org.springframework.hateoas.jaxrs.JaxRsLinkBuilderFactory;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to register a {@link ControllerLinkBuilderFactory} for JaxRS' {@link Path} in case it's on the
|
||||
* classpath.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 0.25
|
||||
*/
|
||||
class JaxRsConfigurationImportSelector implements ImportSelector {
|
||||
|
||||
private static final boolean IS_JAX_RS_PRESENT = ClassUtils.isPresent("javax.ws.rs.Path",
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
|
||||
*/
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
|
||||
return IS_JAX_RS_PRESENT //
|
||||
? new String[] { JaxRsEntityLinksConfiguration.class.getName() } //
|
||||
: new String[0];
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class JaxRsEntityLinksConfiguration {
|
||||
|
||||
@Bean
|
||||
ControllerEntityLinksFactoryBean jaxRsEntityLinks() {
|
||||
|
||||
ControllerEntityLinksFactoryBean factory = new ControllerEntityLinksFactoryBean();
|
||||
factory.setAnnotation(Path.class);
|
||||
factory.setLinkBuilderFactory(new JaxRsLinkBuilderFactory());
|
||||
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,15 +57,15 @@ public class EnableEntityLinksIntegrationTest {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
DelegatingEntityLinks builder;
|
||||
DelegatingEntityLinks links;
|
||||
|
||||
@Test
|
||||
public void initializesDelegatingEntityLinks() {
|
||||
|
||||
assertThat(builder, is(notNullValue()));
|
||||
assertThat(builder.supports(Person.class), is(true));
|
||||
assertThat(builder.supports(Address.class), is(true));
|
||||
assertThat(builder.supports(Object.class), is(false));
|
||||
assertThat(links, is(notNullValue()));
|
||||
assertThat(links.supports(Person.class), is(true));
|
||||
assertThat(links.supports(Address.class), is(true));
|
||||
assertThat(links.supports(Object.class), is(false));
|
||||
}
|
||||
|
||||
@Controller
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -40,19 +40,23 @@ import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar.Jackson2ModuleRegisteringBeanPostProcessor;
|
||||
import org.springframework.hateoas.core.DelegatingEntityLinks;
|
||||
import org.springframework.hateoas.core.DelegatingRelProvider;
|
||||
import org.springframework.hateoas.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.hal.HalLinkDiscoverer;
|
||||
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
@@ -76,7 +80,7 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
@Test
|
||||
public void registersLinkDiscoverers() {
|
||||
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class);
|
||||
ConfigurableApplicationContext context = createApplicationContext(HalConfig.class);
|
||||
LinkDiscoverers discoverers = context.getBean(LinkDiscoverers.class);
|
||||
|
||||
assertThat(discoverers, is(notNullValue()));
|
||||
@@ -97,10 +101,7 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void halSetupIsAppliedToAllTransitiveComponentsInRequestMappingHandlerAdapter() {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class);
|
||||
|
||||
Jackson2ModuleRegisteringBeanPostProcessor postProcessor = new HypermediaSupportBeanDefinitionRegistrar.Jackson2ModuleRegisteringBeanPostProcessor();
|
||||
postProcessor.setBeanFactory(context.getAutowireCapableBeanFactory());
|
||||
ConfigurableApplicationContext context = createApplicationContext(HalConfig.class);
|
||||
|
||||
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
|
||||
|
||||
@@ -133,12 +134,11 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
@Test
|
||||
public void registersHttpMessageConvertersForRestTemplate() {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class);
|
||||
ConfigurableApplicationContext context = createApplicationContext(HalConfig.class);
|
||||
RestTemplate template = context.getBean(RestTemplate.class);
|
||||
|
||||
assertThat(template.getMessageConverters().get(0).getSupportedMediaTypes(),
|
||||
hasItems(MediaTypes.HAL_JSON, MediaTypes.HAL_JSON_UTF8));
|
||||
context.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,42 +147,32 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
@Test
|
||||
public void configuresDefaultObjectMapperForHalToIgnoreUnknownProperties() {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class);
|
||||
ObjectMapper mapper = context.getBean("_halObjectMapper", ObjectMapper.class);
|
||||
ObjectMapper mapper = getObjectMapperFor(MediaTypes.HAL_JSON, createApplicationContext(HalConfig.class));
|
||||
|
||||
assertThat(mapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES), is(false));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyDefaultHalConfigurationRendersSingleItemAsSingleItem() throws JsonProcessingException {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class);
|
||||
|
||||
ObjectMapper mapper = context.getBean("_halObjectMapper", ObjectMapper.class);
|
||||
ObjectMapper mapper = getObjectMapperFor(MediaTypes.HAL_JSON, createApplicationContext(HalConfig.class));
|
||||
|
||||
ResourceSupport resourceSupport = new ResourceSupport();
|
||||
resourceSupport.add(new Link("localhost").withSelfRel());
|
||||
|
||||
assertThat(mapper.writeValueAsString(resourceSupport), is("{\"_links\":{\"self\":{\"href\":\"localhost\"}}}"));
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyRenderSingleLinkAsArrayViaOverridingBean() throws JsonProcessingException {
|
||||
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
RenderLinkAsSingleLinksConfig.class);
|
||||
|
||||
ObjectMapper mapper = context.getBean("_halObjectMapper", ObjectMapper.class);
|
||||
ObjectMapper mapper = getObjectMapperFor(MediaTypes.HAL_JSON,
|
||||
createApplicationContext(RenderLinkAsSingleLinksConfig.class));
|
||||
|
||||
ResourceSupport resourceSupport = new ResourceSupport();
|
||||
resourceSupport.add(new Link("localhost").withSelfRel());
|
||||
|
||||
assertThat(mapper.writeValueAsString(resourceSupport), is("{\"_links\":{\"self\":[{\"href\":\"localhost\"}]}}"));
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
private static void assertEntityLinksSetUp(ApplicationContext context) {
|
||||
@@ -200,10 +190,10 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private static void assertHalSetupForConfigClass(Class<?> configClass) {
|
||||
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(configClass);
|
||||
ConfigurableApplicationContext context = createApplicationContext(HalConfig.class);
|
||||
|
||||
assertEntityLinksSetUp(context);
|
||||
assertThat(context.getBean(LinkDiscoverer.class), is(instanceOf(HalLinkDiscoverer.class)));
|
||||
assertThat(context.getBean(ObjectMapper.class), is(notNullValue()));
|
||||
|
||||
RequestMappingHandlerAdapter rmha = context.getBean(RequestMappingHandlerAdapter.class);
|
||||
assertThat(rmha.getMessageConverters(),
|
||||
@@ -233,20 +223,34 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
throw new IllegalStateException("Unexpected result when looking up argument resolvers!");
|
||||
}
|
||||
|
||||
private static ConfigurableApplicationContext createApplicationContext(Class<?>... configurations) {
|
||||
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(configurations);
|
||||
context.refresh();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static ObjectMapper getObjectMapperFor(MediaType mediaType, ApplicationContext context) {
|
||||
|
||||
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
|
||||
|
||||
for (HttpMessageConverter<?> converter : adapter.getMessageConverters()) {
|
||||
if (converter.getSupportedMediaTypes().contains(mediaType)) {
|
||||
return ((AbstractJackson2HttpMessageConverter) converter).getObjectMapper();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Did not find HttpMessageConverter supporting " + mediaType + "!");
|
||||
}
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@Import(DelegateConfig.class)
|
||||
static class HalConfig {
|
||||
|
||||
static int numberOfMessageConverters = 0;
|
||||
static int numberOfMessageConvertersLegacy = 0;
|
||||
|
||||
@Bean
|
||||
public RequestMappingHandlerAdapter rmh() {
|
||||
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
|
||||
numberOfMessageConverters = adapter.getMessageConverters().size();
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
@@ -264,6 +268,7 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@EnableHypermediaSupport(type = HypermediaType.HAL)
|
||||
static class RenderLinkAsSingleLinksConfig {
|
||||
@@ -272,10 +277,5 @@ public class EnableHypermediaSupportIntegrationTest {
|
||||
HalConfiguration halConfiguration() {
|
||||
return new HalConfiguration().withRenderSingleLinks(AS_ARRAY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestMappingHandlerAdapter rmh() {
|
||||
return new RequestMappingHandlerAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user