Apply HttpMessageConverter auto-config to form part converters
Closes gh-3525
This commit is contained in:
		
							parent
							
								
									30f24eac0a
								
							
						
					
					
						commit
						f770dbab52
					
				| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2014 the original author or authors.
 | 
			
		||||
 * Copyright 2012-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.
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.autoconfigure.web;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
| 
						 | 
				
			
			@ -24,9 +25,11 @@ import java.util.Iterator;
 | 
			
		|||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.converter.HttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
import org.springframework.util.ReflectionUtils;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +48,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupp
 | 
			
		|||
 *
 | 
			
		||||
 * @author Dave Syer
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @author Andy Wilkinson
 | 
			
		||||
 * @see #HttpMessageConverters(HttpMessageConverter...)
 | 
			
		||||
 * @see #HttpMessageConverters(Collection)
 | 
			
		||||
 * @see #getConverters()
 | 
			
		||||
| 
						 | 
				
			
			@ -97,25 +101,37 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
 | 
			
		|||
	 */
 | 
			
		||||
	public HttpMessageConverters(boolean addDefaultConverters,
 | 
			
		||||
			Collection<HttpMessageConverter<?>> converters) {
 | 
			
		||||
		List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
 | 
			
		||||
				addDefaultConverters ? getDefaultConverters()
 | 
			
		||||
						: Collections.<HttpMessageConverter<?>>emptyList());
 | 
			
		||||
		combined = postProcessConverters(combined);
 | 
			
		||||
		this.converters = Collections.unmodifiableList(combined);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private List<HttpMessageConverter<?>> getCombinedConverters(
 | 
			
		||||
			Collection<HttpMessageConverter<?>> converters,
 | 
			
		||||
			List<HttpMessageConverter<?>> defaultConverters) {
 | 
			
		||||
		List<HttpMessageConverter<?>> combined = new ArrayList<HttpMessageConverter<?>>();
 | 
			
		||||
		List<HttpMessageConverter<?>> processing = new ArrayList<HttpMessageConverter<?>>(
 | 
			
		||||
				converters);
 | 
			
		||||
		if (addDefaultConverters) {
 | 
			
		||||
			for (HttpMessageConverter<?> defaultConverter : getDefaultConverters()) {
 | 
			
		||||
				Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
 | 
			
		||||
				while (iterator.hasNext()) {
 | 
			
		||||
					HttpMessageConverter<?> candidate = iterator.next();
 | 
			
		||||
					if (isReplacement(defaultConverter, candidate)) {
 | 
			
		||||
						combined.add(candidate);
 | 
			
		||||
						iterator.remove();
 | 
			
		||||
					}
 | 
			
		||||
		for (HttpMessageConverter<?> defaultConverter : defaultConverters) {
 | 
			
		||||
			Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
 | 
			
		||||
			while (iterator.hasNext()) {
 | 
			
		||||
				HttpMessageConverter<?> candidate = iterator.next();
 | 
			
		||||
				if (isReplacement(defaultConverter, candidate)) {
 | 
			
		||||
					combined.add(candidate);
 | 
			
		||||
					iterator.remove();
 | 
			
		||||
				}
 | 
			
		||||
				combined.add(defaultConverter);
 | 
			
		||||
			}
 | 
			
		||||
			combined.add(defaultConverter);
 | 
			
		||||
			if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) {
 | 
			
		||||
				configurePartConverters(
 | 
			
		||||
						(AllEncompassingFormHttpMessageConverter) defaultConverter,
 | 
			
		||||
						converters);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		combined.addAll(0, processing);
 | 
			
		||||
		combined = postProcessConverters(combined);
 | 
			
		||||
		this.converters = Collections.unmodifiableList(combined);
 | 
			
		||||
		return combined;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean isReplacement(HttpMessageConverter<?> defaultConverter,
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +144,28 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
 | 
			
		|||
		return ClassUtils.isAssignableValue(defaultConverter.getClass(), candidate);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void configurePartConverters(
 | 
			
		||||
			AllEncompassingFormHttpMessageConverter formConverter,
 | 
			
		||||
			Collection<HttpMessageConverter<?>> converters) {
 | 
			
		||||
		List<HttpMessageConverter<?>> partConverters = extractPartConverters(
 | 
			
		||||
				formConverter);
 | 
			
		||||
		List<HttpMessageConverter<?>> combinedConverters = getCombinedConverters(
 | 
			
		||||
				converters, partConverters);
 | 
			
		||||
		combinedConverters = postProcessPartConverters(combinedConverters);
 | 
			
		||||
		formConverter.setPartConverters(combinedConverters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private List<HttpMessageConverter<?>> extractPartConverters(
 | 
			
		||||
			AllEncompassingFormHttpMessageConverter formConverter) {
 | 
			
		||||
		Field field = ReflectionUtils.findField(
 | 
			
		||||
				AllEncompassingFormHttpMessageConverter.class, "partConverters");
 | 
			
		||||
		ReflectionUtils.makeAccessible(field);
 | 
			
		||||
		@SuppressWarnings("unchecked")
 | 
			
		||||
		List<HttpMessageConverter<?>> partConverters = (List<HttpMessageConverter<?>>) ReflectionUtils
 | 
			
		||||
				.getField(field, formConverter);
 | 
			
		||||
		return partConverters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Method that can be used to post-process the {@link HttpMessageConverter} list
 | 
			
		||||
	 * before it is used.
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +177,19 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
 | 
			
		|||
		return converters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Method that can be used to post-process the {@link HttpMessageConverter} list
 | 
			
		||||
	 * before it is used to configure the part converters of
 | 
			
		||||
	 * {@link AllEncompassingFormHttpMessageConverter}.
 | 
			
		||||
	 * @param converters a mutable list of the converters that will be used.
 | 
			
		||||
	 * @return the final converts list to use
 | 
			
		||||
	 * @since 1.3.0
 | 
			
		||||
	 */
 | 
			
		||||
	protected List<HttpMessageConverter<?>> postProcessPartConverters(
 | 
			
		||||
			List<HttpMessageConverter<?>> converters) {
 | 
			
		||||
		return converters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private List<HttpMessageConverter<?>> getDefaultConverters() {
 | 
			
		||||
		List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
 | 
			
		||||
		if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.web;
 | 
			
		|||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +33,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
 | 
			
		|||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
 | 
			
		||||
import org.springframework.test.util.ReflectionTestUtils;
 | 
			
		||||
 | 
			
		||||
import static org.hamcrest.Matchers.equalTo;
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +102,19 @@ public class HttpMessageConvertersTests {
 | 
			
		|||
		assertEquals(converter2, converters.getConverters().get(1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void convertersAreAddedToFormPartConverter() {
 | 
			
		||||
		HttpMessageConverter<?> converter1 = mock(HttpMessageConverter.class);
 | 
			
		||||
		HttpMessageConverter<?> converter2 = mock(HttpMessageConverter.class);
 | 
			
		||||
		List<HttpMessageConverter<?>> converters = new HttpMessageConverters(converter1,
 | 
			
		||||
				converter2).getConverters();
 | 
			
		||||
		List<HttpMessageConverter<?>> partConverters = extractFormPartConverters(
 | 
			
		||||
				converters);
 | 
			
		||||
		assertTrue(partConverters.contains(converter1));
 | 
			
		||||
		assertEquals(converter1, partConverters.get(0));
 | 
			
		||||
		assertEquals(converter2, partConverters.get(1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void postProcessConverters() throws Exception {
 | 
			
		||||
		HttpMessageConverters converters = new HttpMessageConverters() {
 | 
			
		||||
| 
						 | 
				
			
			@ -129,4 +144,52 @@ public class HttpMessageConvertersTests {
 | 
			
		|||
						MappingJackson2HttpMessageConverter.class)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void postProcessPartConverters() throws Exception {
 | 
			
		||||
		HttpMessageConverters converters = new HttpMessageConverters() {
 | 
			
		||||
			@Override
 | 
			
		||||
			protected List<HttpMessageConverter<?>> postProcessPartConverters(
 | 
			
		||||
					List<HttpMessageConverter<?>> converters) {
 | 
			
		||||
				for (Iterator<HttpMessageConverter<?>> iterator = converters
 | 
			
		||||
						.iterator(); iterator.hasNext();) {
 | 
			
		||||
					if (iterator
 | 
			
		||||
							.next() instanceof MappingJackson2XmlHttpMessageConverter) {
 | 
			
		||||
						iterator.remove();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return converters;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		List<Class<?>> converterClasses = new ArrayList<Class<?>>();
 | 
			
		||||
		for (HttpMessageConverter<?> converter : extractFormPartConverters(
 | 
			
		||||
				converters.getConverters())) {
 | 
			
		||||
			converterClasses.add(converter.getClass());
 | 
			
		||||
		}
 | 
			
		||||
		assertThat(converterClasses,
 | 
			
		||||
				equalTo(Arrays.<Class<?>>asList(ByteArrayHttpMessageConverter.class,
 | 
			
		||||
						StringHttpMessageConverter.class,
 | 
			
		||||
						ResourceHttpMessageConverter.class,
 | 
			
		||||
						SourceHttpMessageConverter.class,
 | 
			
		||||
						MappingJackson2HttpMessageConverter.class)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	private List<HttpMessageConverter<?>> extractFormPartConverters(
 | 
			
		||||
			List<HttpMessageConverter<?>> converters) {
 | 
			
		||||
		AllEncompassingFormHttpMessageConverter formConverter = findFormConverter(
 | 
			
		||||
				converters);
 | 
			
		||||
		return (List<HttpMessageConverter<?>>) ReflectionTestUtils.getField(formConverter,
 | 
			
		||||
				"partConverters");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private AllEncompassingFormHttpMessageConverter findFormConverter(
 | 
			
		||||
			Collection<HttpMessageConverter<?>> converters) {
 | 
			
		||||
		for (HttpMessageConverter<?> converter : converters) {
 | 
			
		||||
			if (converter instanceof AllEncompassingFormHttpMessageConverter) {
 | 
			
		||||
				return (AllEncompassingFormHttpMessageConverter) converter;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue