/* * Copyright 2013-2015 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.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import org.hamcrest.Matchers; 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.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.LinkDiscoverer; import org.springframework.hateoas.LinkDiscoverers; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RelProvider; 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.HalLinkDiscoverer; import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.client.RestTemplate; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; /** * Integration tests for {@link EnableHypermediaSupport}. * * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) public class EnableHypermediaSupportIntegrationTest { @Test public void bootstrapHalConfiguration() { assertHalSetupForConfigClass(HalConfig.class); } @Test public void registersLinkDiscoverers() { ApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class); LinkDiscoverers discoverers = context.getBean(LinkDiscoverers.class); assertThat(discoverers, is(notNullValue())); assertThat(discoverers.getLinkDiscovererFor(MediaTypes.HAL_JSON), is(instanceOf(HalLinkDiscoverer.class))); assertRelProvidersSetUp(context); } @Test public void bootstrapsHalConfigurationForSubclass() { assertHalSetupForConfigClass(ExtendedHalConfig.class); } /** * @see #134, #219 */ @Test @SuppressWarnings("unchecked") public void halSetupIsAppliedToAllTransitiveComponentsInRequestMappingHandlerAdapter() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class); Jackson2ModuleRegisteringBeanPostProcessor postProcessor = new HypermediaSupportBeanDefinitionRegistrar.Jackson2ModuleRegisteringBeanPostProcessor(); postProcessor.setBeanFactory(context.getAutowireCapableBeanFactory()); RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class); assertThat(adapter.getMessageConverters().get(0).getSupportedMediaTypes(), hasItem(MediaTypes.HAL_JSON)); boolean found = false; for (HandlerMethodArgumentResolver resolver : getResolvers(adapter)) { if (resolver instanceof AbstractMessageConverterMethodArgumentResolver) { found = true; AbstractMessageConverterMethodArgumentResolver processor = (AbstractMessageConverterMethodArgumentResolver) resolver; List<HttpMessageConverter<?>> converters = (List<HttpMessageConverter<?>>) ReflectionTestUtils .getField(processor, "messageConverters"); assertThat(converters.get(0), is(instanceOf(TypeConstrainedMappingJackson2HttpMessageConverter.class))); assertThat(converters.get(0).getSupportedMediaTypes(), hasItem(MediaTypes.HAL_JSON)); } } assertThat(found, is(true)); } /** * @see #293 */ @Test public void registersHttpMessageConvertersForRestTemplate() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class); RestTemplate template = context.getBean(RestTemplate.class); assertThat(template.getMessageConverters().get(0).getSupportedMediaTypes(), hasItem(MediaTypes.HAL_JSON)); context.close(); } /** * @see #341 */ @Test public void configuresDefaultObjectMapperForHalToIgnoreUnknownProperties() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class); ObjectMapper mapper = context.getBean("_halObjectMapper", ObjectMapper.class); assertThat(mapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES), is(false)); context.close(); } private static void assertEntityLinksSetUp(ApplicationContext context) { Map<String, EntityLinks> discoverers = context.getBeansOfType(EntityLinks.class); assertThat(discoverers.values(), Matchers.<EntityLinks>hasItem(instanceOf(DelegatingEntityLinks.class))); } private static void assertRelProvidersSetUp(ApplicationContext context) { Map<String, RelProvider> discoverers = context.getBeansOfType(RelProvider.class); assertThat(discoverers.values(), Matchers.<RelProvider>hasItem(instanceOf(DelegatingRelProvider.class))); } @SuppressWarnings({ "unchecked" }) private static void assertHalSetupForConfigClass(Class<?> configClass) { ApplicationContext context = new AnnotationConfigApplicationContext(configClass); 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(), Matchers.<HttpMessageConverter<?>>hasItems(instanceOf(MappingJackson2HttpMessageConverter.class))); } /** * Method to mitigate API changes between Spring 3.2 and 4.0. * * @param adapter * @return */ @SuppressWarnings("unchecked") private static List<HandlerMethodArgumentResolver> getResolvers(RequestMappingHandlerAdapter adapter) { Method method = ReflectionUtils.findMethod(RequestMappingHandlerAdapter.class, "getArgumentResolvers"); Object result = ReflectionUtils.invokeMethod(method, adapter); if (result instanceof List) { return (List<HandlerMethodArgumentResolver>) result; } if (result instanceof HandlerMethodArgumentResolverComposite) { return ((HandlerMethodArgumentResolverComposite) result).getResolvers(); } throw new IllegalStateException("Unexpected result when looking up argument resolvers!"); } @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(); } } @Configuration static class ExtendedHalConfig extends HalConfig { } @Configuration @EnableHypermediaSupport(type = HypermediaType.HAL) static class DelegateConfig { } }