001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.hateoas; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.List; 022 023import javax.annotation.PostConstruct; 024 025import org.springframework.beans.BeansException; 026import org.springframework.beans.factory.BeanFactory; 027import org.springframework.beans.factory.BeanFactoryAware; 028import org.springframework.beans.factory.ListableBeanFactory; 029import org.springframework.beans.factory.config.BeanPostProcessor; 030import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 031import org.springframework.context.annotation.Bean; 032import org.springframework.context.annotation.Configuration; 033import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; 034import org.springframework.http.MediaType; 035import org.springframework.http.converter.AbstractHttpMessageConverter; 036import org.springframework.http.converter.HttpMessageConverter; 037import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; 038 039/** 040 * Configuration for {@link HttpMessageConverter HttpMessageConverters} when hypermedia is 041 * enabled. 042 * 043 * @author Andy Wilkinson 044 */ 045@Configuration 046public class HypermediaHttpMessageConverterConfiguration { 047 048 @Bean 049 @ConditionalOnProperty(prefix = "spring.hateoas", name = "use-hal-as-default-json-media-type", matchIfMissing = true) 050 public static HalMessageConverterSupportedMediaTypesCustomizer halMessageConverterSupportedMediaTypeCustomizer() { 051 return new HalMessageConverterSupportedMediaTypesCustomizer(); 052 } 053 054 /** 055 * Updates any {@link TypeConstrainedMappingJackson2HttpMessageConverter}s to support 056 * {@code application/json} in addition to {@code application/hal+json}. Cannot be a 057 * {@link BeanPostProcessor} as processing must be performed after 058 * {@code Jackson2ModuleRegisteringBeanPostProcessor} has registered the converter and 059 * it is unordered. 060 */ 061 private static class HalMessageConverterSupportedMediaTypesCustomizer 062 implements BeanFactoryAware { 063 064 private volatile BeanFactory beanFactory; 065 066 @PostConstruct 067 public void configureHttpMessageConverters() { 068 if (this.beanFactory instanceof ListableBeanFactory) { 069 configureHttpMessageConverters(((ListableBeanFactory) this.beanFactory) 070 .getBeansOfType(RequestMappingHandlerAdapter.class).values()); 071 } 072 } 073 074 private void configureHttpMessageConverters( 075 Collection<RequestMappingHandlerAdapter> handlerAdapters) { 076 for (RequestMappingHandlerAdapter handlerAdapter : handlerAdapters) { 077 for (HttpMessageConverter<?> messageConverter : handlerAdapter 078 .getMessageConverters()) { 079 configureHttpMessageConverter(messageConverter); 080 } 081 } 082 } 083 084 private void configureHttpMessageConverter(HttpMessageConverter<?> converter) { 085 if (converter instanceof TypeConstrainedMappingJackson2HttpMessageConverter) { 086 List<MediaType> supportedMediaTypes = new ArrayList<>( 087 converter.getSupportedMediaTypes()); 088 if (!supportedMediaTypes.contains(MediaType.APPLICATION_JSON)) { 089 supportedMediaTypes.add(MediaType.APPLICATION_JSON); 090 } 091 ((AbstractHttpMessageConverter<?>) converter) 092 .setSupportedMediaTypes(supportedMediaTypes); 093 } 094 } 095 096 @Override 097 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 098 this.beanFactory = beanFactory; 099 } 100 101 } 102 103}