Fix package cycle among http message converters

This change introduces a new AllEncompassingFormHttpMessageConverter
class that adds JSON and XML converters for individual mime parts of
a multi-part request. The new converter is used in place of the
previously used XmlAwareFormHttpMessageConverter.

Issue: SPR-10055
This commit is contained in:
Rossen Stoyanchev 2012-12-07 18:12:11 -05:00
parent d309bb4bbb
commit 85a552daed
13 changed files with 80 additions and 28 deletions

View File

@ -36,15 +36,11 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
/**
* Implementation of {@link HttpMessageConverter} that can handle form data, including multipart form data (i.e. file
@ -76,14 +72,6 @@ import org.springframework.web.client.RestTemplate;
*/
public class FormHttpMessageConverter implements HttpMessageConverter<MultiValueMap<String, ?>> {
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader());
private static final boolean jacksonPresent =
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", RestTemplate.class.getClassLoader()) &&
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", RestTemplate.class.getClassLoader());
private static final byte[] BOUNDARY_CHARS =
new byte[]{'-', '_', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A',
@ -108,12 +96,6 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
stringHttpMessageConverter.setWriteAcceptCharset(false);
this.partConverters.add(stringHttpMessageConverter);
this.partConverters.add(new ResourceHttpMessageConverter());
if (jackson2Present) {
this.partConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (jacksonPresent) {
this.partConverters.add(new MappingJacksonHttpMessageConverter());
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2012 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.http.converter.support;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.util.ClassUtils;
/**
* Extension of {@link org.springframework.http.converter.FormHttpMessageConverter},
* adding support for XML and JSON-based parts.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter {
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jacksonPresent =
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder", AllEncompassingFormHttpMessageConverter.class.getClassLoader());
public AllEncompassingFormHttpMessageConverter() {
addPartConverter(new SourceHttpMessageConverter());
if (jaxb2Present) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (jacksonPresent) {
addPartConverter(new MappingJacksonHttpMessageConverter());
}
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.http.converter.xml;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
/**
* Extension of {@link org.springframework.http.converter.FormHttpMessageConverter},
@ -24,6 +25,7 @@ import org.springframework.http.converter.FormHttpMessageConverter;
*
* @author Juergen Hoeller
* @since 3.0.3
* @deprecated in favor of {@link AllEncompassingFormHttpMessageConverter}
*/
public class XmlAwareFormHttpMessageConverter extends FormHttpMessageConverter {

View File

@ -45,6 +45,7 @@ import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
@ -151,7 +152,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter());
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());

View File

@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
@ -60,7 +61,7 @@ import org.springframework.util.MultiValueMap;
*/
public class HttpPutFormContentFilter extends OncePerRequestFilter {
private final FormHttpMessageConverter formConverter = new XmlAwareFormHttpMessageConverter();
private final FormHttpMessageConverter formConverter = new AllEncompassingFormHttpMessageConverter();
/**
* The default character set to use for reading form data.

View File

@ -40,6 +40,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -55,7 +56,7 @@ public class FormHttpMessageConverterTests {
@Before
public void setUp() {
converter = new XmlAwareFormHttpMessageConverter();
converter = new AllEncompassingFormHttpMessageConverter();
}
@Test

View File

@ -39,6 +39,7 @@ import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
@ -413,7 +414,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
messageConverters.add(createConverterBeanDefinition(AllEncompassingFormHttpMessageConverter.class, source));
if (romePresent) {
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));

View File

@ -46,6 +46,7 @@ import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
@ -531,7 +532,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new XmlAwareFormHttpMessageConverter());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());

View File

@ -37,6 +37,7 @@ import org.springframework.core.OrderComparator;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.web.accept.ContentNegotiationManager;
@ -104,7 +105,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
/**

View File

@ -45,6 +45,7 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.ui.ModelMap;
@ -181,7 +182,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
/**

View File

@ -41,6 +41,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
@ -102,7 +103,7 @@ public class RequestPartIntegrationTests {
@Before
public void setUp() {
XmlAwareFormHttpMessageConverter converter = new XmlAwareFormHttpMessageConverter();
AllEncompassingFormHttpMessageConverter converter = new AllEncompassingFormHttpMessageConverter();
converter.setPartConverters(Arrays.<HttpMessageConverter<?>>asList(
new ResourceHttpMessageConverter(), new MappingJacksonHttpMessageConverter()));

View File

@ -32,6 +32,7 @@ import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
@ -119,7 +120,7 @@ public class RequestResponseBodyMethodProcessorTests {
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new XmlAwareFormHttpMessageConverter());
converters.add(new AllEncompassingFormHttpMessageConverter());
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
@SuppressWarnings("unchecked")

View File

@ -105,7 +105,7 @@ Changes in version 3.2 RC1 (2012-11-04)
* use concurrent cache to improve performance of GenericTypeResolver (SPR-8701)
* cache and late resolve annotations on bean properties to improve performance (SPR-9166)
* allow PropertyResolver implementations to ignore unresolvable ${placeholders} (SPR-9569)
* FormHttpMessageConverter now adds Jackson JSON converters if available on the classpath (SPR-10055)
* AllEncompassingFormHttpMessageConverter now adds XML/JSON converters for individual mime parts (SPR-10055)
Changes in version 3.2 M2 (2012-09-11)
--------------------------------------