Add HttpMessageConverters
Prior to this commit, Spring Web would configure `HttpMessageConverter<?>` collections on clients like `RestTemplate` and on the server infrastructure, in `WebMvcConfigurationSupport`. This commit introduces a high-level construct for building and configuring ordered collections of converters. This includes: * configuration of well-known converters with classpath detection * configuration of shared converters, or client/server specific * configuration of custom converters See gh-33894
This commit is contained in:
parent
4b44a34692
commit
1af25e9cb1
|
@ -0,0 +1,604 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-present 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
|
||||||
|
*
|
||||||
|
* https://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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.cbor.JacksonCborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.cbor.KotlinSerializationCborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.protobuf.KotlinSerializationProtobufHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.smile.JacksonSmileHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.yaml.JacksonYamlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation for {@link HttpMessageConverters}.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
class DefaultHttpMessageConverters implements HttpMessageConverters {
|
||||||
|
|
||||||
|
private final List<HttpMessageConverter<?>> clientMessageConverters;
|
||||||
|
|
||||||
|
private final List<HttpMessageConverter<?>> serverMessageConverters;
|
||||||
|
|
||||||
|
DefaultHttpMessageConverters(List<HttpMessageConverter<?>> clientMessageConverters, List<HttpMessageConverter<?>> serverMessageConverters) {
|
||||||
|
this.clientMessageConverters = clientMessageConverters;
|
||||||
|
this.serverMessageConverters = serverMessageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<HttpMessageConverter<?>> forClient() {
|
||||||
|
return this.clientMessageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<HttpMessageConverter<?>> forServer() {
|
||||||
|
return this.serverMessageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultBuilder implements HttpMessageConverters.Builder {
|
||||||
|
|
||||||
|
private final DefaultMessageConverterConfigurer commonMessageConverters;
|
||||||
|
|
||||||
|
private final DefaultClientMessageConverterConfigurer clientMessageConverterConfigurer;
|
||||||
|
|
||||||
|
private final DefaultServerMessageConverterConfigurer serverMessageConverterConfigurer;
|
||||||
|
|
||||||
|
|
||||||
|
DefaultBuilder(boolean registerDefaults) {
|
||||||
|
this(registerDefaults, DefaultHttpMessageConverters.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultBuilder(boolean registerDefaults, ClassLoader classLoader) {
|
||||||
|
this.commonMessageConverters = new DefaultMessageConverterConfigurer();
|
||||||
|
this.clientMessageConverterConfigurer = new DefaultClientMessageConverterConfigurer(this.commonMessageConverters);
|
||||||
|
this.serverMessageConverterConfigurer = new DefaultServerMessageConverterConfigurer(this.commonMessageConverters);
|
||||||
|
if (registerDefaults) {
|
||||||
|
this.commonMessageConverters.registerDefaults(classLoader);
|
||||||
|
this.clientMessageConverterConfigurer.registerDefaults(classLoader);
|
||||||
|
this.serverMessageConverterConfigurer.registerDefaults(classLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder configureClient(Consumer<ClientMessageConverterConfigurer> consumer) {
|
||||||
|
consumer.accept(this.clientMessageConverterConfigurer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder configureServer(Consumer<ServerMessageConverterConfigurer> consumer) {
|
||||||
|
consumer.accept(this.serverMessageConverterConfigurer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder stringMessageConverter(HttpMessageConverter<?> stringMessageConverter) {
|
||||||
|
this.commonMessageConverters.setStringMessageConverter(stringMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultBuilder jsonMessageConverter(HttpMessageConverter<?> jsonMessageConverter) {
|
||||||
|
this.commonMessageConverters.setJsonMessageConverter(jsonMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultBuilder xmlMessageConverter(HttpMessageConverter<?> xmlMessageConverter) {
|
||||||
|
this.commonMessageConverters.setXmlMessageConverter(xmlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultBuilder smileMessageConverter(HttpMessageConverter<?> smileMessageConverter) {
|
||||||
|
this.commonMessageConverters.setSmileMessageConverter(smileMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder cborMessageConverter(HttpMessageConverter<?> cborMessageConverter) {
|
||||||
|
this.commonMessageConverters.setCborMessageConverter(cborMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder yamlMessageConverter(HttpMessageConverter<?> yamlMessageConverter) {
|
||||||
|
this.commonMessageConverters.setYamlMessageConverter(yamlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultBuilder additionalMessageConverter(HttpMessageConverter<?> customConverter) {
|
||||||
|
Assert.notNull(customConverter, "'customConverter' must not be null");
|
||||||
|
this.commonMessageConverters.additionalMessageConverters.add(customConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultHttpMessageConverters build() {
|
||||||
|
return new DefaultHttpMessageConverters(this.clientMessageConverterConfigurer.getMessageConverters(),
|
||||||
|
this.serverMessageConverterConfigurer.getMessageConverters());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultMessageConverterConfigurer {
|
||||||
|
|
||||||
|
private final @Nullable DefaultMessageConverterConfigurer inheritedMessageConverters;
|
||||||
|
|
||||||
|
private @Nullable ByteArrayHttpMessageConverter byteArrayMessageConverter;
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> stringMessageConverter;
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> resourceMessageConverters = Collections.emptyList();
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> jsonMessageConverter;
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> xmlMessageConverter;
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> smileMessageConverter;
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> cborMessageConverter;
|
||||||
|
|
||||||
|
private @Nullable HttpMessageConverter<?> yamlMessageConverter;
|
||||||
|
|
||||||
|
private final List<HttpMessageConverter<?>> additionalMessageConverters = new ArrayList<>();
|
||||||
|
|
||||||
|
DefaultMessageConverterConfigurer() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultMessageConverterConfigurer(@Nullable DefaultMessageConverterConfigurer inheritedMessageConverters) {
|
||||||
|
this.inheritedMessageConverters = inheritedMessageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStringMessageConverter(HttpMessageConverter<?> stringMessageConverter) {
|
||||||
|
Assert.isTrue(stringMessageConverter.getSupportedMediaTypes().contains(MediaType.TEXT_PLAIN),
|
||||||
|
"stringMessageConverter should support 'text/plain'");
|
||||||
|
this.stringMessageConverter = stringMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setJsonMessageConverter(HttpMessageConverter<?> jsonMessageConverter) {
|
||||||
|
Assert.isTrue(jsonMessageConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_JSON),
|
||||||
|
"jsonMessageConverter should support 'application/json'");
|
||||||
|
this.jsonMessageConverter = jsonMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setXmlMessageConverter(HttpMessageConverter<?> xmlMessageConverter) {
|
||||||
|
Assert.isTrue(xmlMessageConverter.getSupportedMediaTypes().contains(MediaType.TEXT_XML),
|
||||||
|
"xmlMessageConverter should support 'text/xml'");
|
||||||
|
this.xmlMessageConverter = xmlMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSmileMessageConverter(HttpMessageConverter<?> smileMessageConverter) {
|
||||||
|
Assert.isTrue(smileMessageConverter.getSupportedMediaTypes().contains(new MediaType("application", "x-jackson-smile")),
|
||||||
|
"smileMessageConverter should support 'application/x-jackson-smile'");
|
||||||
|
this.smileMessageConverter = smileMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCborMessageConverter(HttpMessageConverter<?> cborMessageConverter) {
|
||||||
|
Assert.isTrue(cborMessageConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_CBOR),
|
||||||
|
"cborMessageConverter should support 'application/cbor'");
|
||||||
|
this.cborMessageConverter = cborMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setYamlMessageConverter(HttpMessageConverter<?> yamlMessageConverter) {
|
||||||
|
Assert.isTrue(yamlMessageConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_YAML),
|
||||||
|
"yamlMessageConverter should support 'application/yaml'");
|
||||||
|
this.yamlMessageConverter = yamlMessageConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> getBaseConverters() {
|
||||||
|
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||||
|
if (this.byteArrayMessageConverter != null) {
|
||||||
|
converters.add(this.byteArrayMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.byteArrayMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.byteArrayMessageConverter);
|
||||||
|
}
|
||||||
|
if (this.stringMessageConverter != null) {
|
||||||
|
converters.add(this.stringMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.stringMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.stringMessageConverter);
|
||||||
|
}
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> getCoreConverters() {
|
||||||
|
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||||
|
if (this.jsonMessageConverter != null) {
|
||||||
|
converters.add(this.jsonMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.jsonMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.jsonMessageConverter);
|
||||||
|
}
|
||||||
|
if (this.smileMessageConverter != null) {
|
||||||
|
converters.add(this.smileMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.smileMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.smileMessageConverter);
|
||||||
|
}
|
||||||
|
if (this.cborMessageConverter!= null) {
|
||||||
|
converters.add(this.cborMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.cborMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.cborMessageConverter);
|
||||||
|
}
|
||||||
|
if (this.yamlMessageConverter!= null) {
|
||||||
|
converters.add(this.yamlMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.yamlMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.yamlMessageConverter);
|
||||||
|
}
|
||||||
|
if (this.xmlMessageConverter!= null) {
|
||||||
|
converters.add(this.xmlMessageConverter);
|
||||||
|
}
|
||||||
|
else if (this.inheritedMessageConverters != null &&
|
||||||
|
this.inheritedMessageConverters.xmlMessageConverter != null) {
|
||||||
|
converters.add(this.inheritedMessageConverters.xmlMessageConverter);
|
||||||
|
}
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> getCustomConverters() {
|
||||||
|
List<HttpMessageConverter<?>> result = new ArrayList<>(this.additionalMessageConverters);
|
||||||
|
if (this.inheritedMessageConverters != null) {
|
||||||
|
result.addAll(this.inheritedMessageConverters.additionalMessageConverters);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerDefaults(ClassLoader classLoader) {
|
||||||
|
this.byteArrayMessageConverter = new ByteArrayHttpMessageConverter();
|
||||||
|
this.stringMessageConverter = new StringHttpMessageConverter();
|
||||||
|
|
||||||
|
if (isJacksonPresent(classLoader)) {
|
||||||
|
this.jsonMessageConverter = new JacksonJsonHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJackson2Present(classLoader)) {
|
||||||
|
this.jsonMessageConverter = new MappingJackson2HttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isGsonPresent(classLoader)) {
|
||||||
|
this.jsonMessageConverter = new GsonHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJsonbPresent(classLoader)) {
|
||||||
|
this.jsonMessageConverter = new JsonbHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isKotlinSerializationJsonPresent(classLoader)) {
|
||||||
|
this.jsonMessageConverter = new KotlinSerializationJsonHttpMessageConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJacksonXmlPresent(classLoader)) {
|
||||||
|
this.xmlMessageConverter = new JacksonXmlHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJackson2XmlPresent(classLoader)) {
|
||||||
|
this.xmlMessageConverter = new MappingJackson2XmlHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJaxb2Present(classLoader)) {
|
||||||
|
this.xmlMessageConverter = new Jaxb2RootElementHttpMessageConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJacksonSmilePresent(classLoader)) {
|
||||||
|
this.smileMessageConverter = new JacksonSmileHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJackson2SmilePresent(classLoader)) {
|
||||||
|
this.smileMessageConverter = new MappingJackson2SmileHttpMessageConverter();
|
||||||
|
}
|
||||||
|
if (isJacksonCborPresent(classLoader)) {
|
||||||
|
this.cborMessageConverter = new JacksonCborHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJackson2CborPresent(classLoader)) {
|
||||||
|
this.cborMessageConverter = new MappingJackson2CborHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isKotlinSerializationCborPresent(classLoader)) {
|
||||||
|
this.cborMessageConverter = new KotlinSerializationCborHttpMessageConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJacksonYamlPresent(classLoader)) {
|
||||||
|
this.yamlMessageConverter = new JacksonYamlHttpMessageConverter();
|
||||||
|
}
|
||||||
|
else if (isJackson2YamlPresent(classLoader)) {
|
||||||
|
this.yamlMessageConverter = new MappingJackson2YamlHttpMessageConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isKotlinSerializationProtobufPresent(classLoader)) {
|
||||||
|
this.additionalMessageConverters.add(new KotlinSerializationProtobufHttpMessageConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRomePresent(classLoader)) {
|
||||||
|
this.additionalMessageConverters.add(new AtomFeedHttpMessageConverter());
|
||||||
|
this.additionalMessageConverters.add(new RssChannelHttpMessageConverter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRomePresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJacksonPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJackson2Present(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||||
|
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isGsonPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.google.gson.Gson", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJsonbPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isKotlinSerializationJsonPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJacksonSmilePresent(ClassLoader classLoader) {
|
||||||
|
return isJacksonPresent(classLoader) && ClassUtils.isPresent("tools.jackson.dataformat.smile.SmileMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJackson2SmilePresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJaxb2Present(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJacksonXmlPresent(ClassLoader classLoader) {
|
||||||
|
return isJacksonPresent(classLoader) && ClassUtils.isPresent("tools.jackson.dataformat.xml.XmlMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJackson2XmlPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJacksonCborPresent(ClassLoader classLoader) {
|
||||||
|
return isJacksonPresent(classLoader) && ClassUtils.isPresent("tools.jackson.dataformat.cbor.CBORMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJackson2CborPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJacksonYamlPresent(ClassLoader classLoader) {
|
||||||
|
return isJacksonPresent(classLoader) && ClassUtils.isPresent("tools.jackson.dataformat.yaml.YAMLMapper", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJackson2YamlPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isKotlinSerializationCborPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isKotlinSerializationProtobufPresent(ClassLoader classLoader) {
|
||||||
|
return ClassUtils.isPresent("kotlinx.serialization.protobuf.ProtoBuf", classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultClientMessageConverterConfigurer extends DefaultMessageConverterConfigurer implements ClientMessageConverterConfigurer {
|
||||||
|
|
||||||
|
private @Nullable Consumer<HttpMessageConverter<?>> configurer;
|
||||||
|
|
||||||
|
private final DefaultMessageConverterConfigurer clientMessageConverters;
|
||||||
|
|
||||||
|
|
||||||
|
public DefaultClientMessageConverterConfigurer(DefaultMessageConverterConfigurer parentMessageConverters) {
|
||||||
|
this.clientMessageConverters = new DefaultMessageConverterConfigurer(parentMessageConverters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer stringMessageConverter(HttpMessageConverter<?> stringMessageConverter) {
|
||||||
|
this.clientMessageConverters.setStringMessageConverter(stringMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer jsonMessageConverter(HttpMessageConverter<?> jsonMessageConverter) {
|
||||||
|
this.clientMessageConverters.setJsonMessageConverter(jsonMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer xmlMessageConverter(HttpMessageConverter<?> xmlMessageConverter) {
|
||||||
|
this.clientMessageConverters.setXmlMessageConverter(xmlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer smileMessageConverter(HttpMessageConverter<?> smileMessageConverter) {
|
||||||
|
this.clientMessageConverters.setSmileMessageConverter(smileMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer cborMessageConverter(HttpMessageConverter<?> cborMessageConverter) {
|
||||||
|
this.clientMessageConverters.setCborMessageConverter(cborMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer yamlMessageConverter(HttpMessageConverter<?> yamlMessageConverter) {
|
||||||
|
this.clientMessageConverters.setYamlMessageConverter(yamlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter<?> customConverter) {
|
||||||
|
Assert.notNull(customConverter, "'customConverter' must not be null");
|
||||||
|
this.clientMessageConverters.additionalMessageConverters.add(customConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientMessageConverterConfigurer configureClientMessageConverters(Consumer<HttpMessageConverter<?>> configurer) {
|
||||||
|
this.configurer = (this.configurer != null) ? configurer.andThen(this.configurer) : configurer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void registerDefaults(ClassLoader classLoader) {
|
||||||
|
this.resourceMessageConverters = Collections.singletonList(new ResourceHttpMessageConverter(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> getMessageConverters() {
|
||||||
|
List<HttpMessageConverter<?>> allConverters = new ArrayList<>();
|
||||||
|
List<HttpMessageConverter<?>> partConverters = new ArrayList<>();
|
||||||
|
|
||||||
|
partConverters.addAll(this.clientMessageConverters.getCoreConverters());
|
||||||
|
partConverters.addAll(this.clientMessageConverters.getCustomConverters());
|
||||||
|
|
||||||
|
allConverters.addAll(this.clientMessageConverters.getBaseConverters());
|
||||||
|
allConverters.addAll(this.resourceMessageConverters);
|
||||||
|
if (!partConverters.isEmpty()) {
|
||||||
|
allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters));
|
||||||
|
}
|
||||||
|
allConverters.addAll(this.clientMessageConverters.getCoreConverters());
|
||||||
|
allConverters.addAll(this.clientMessageConverters.getCustomConverters());
|
||||||
|
|
||||||
|
if (this.configurer != null) {
|
||||||
|
allConverters.forEach(this.configurer);
|
||||||
|
}
|
||||||
|
return allConverters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultServerMessageConverterConfigurer extends DefaultMessageConverterConfigurer implements ServerMessageConverterConfigurer {
|
||||||
|
|
||||||
|
private @Nullable Consumer<HttpMessageConverter<?>> configurer;
|
||||||
|
|
||||||
|
private final DefaultMessageConverterConfigurer serverMessageConverters;
|
||||||
|
|
||||||
|
|
||||||
|
DefaultServerMessageConverterConfigurer(DefaultMessageConverterConfigurer commonMessageConverters) {
|
||||||
|
this.serverMessageConverters = new DefaultMessageConverterConfigurer(commonMessageConverters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer stringMessageConverter(HttpMessageConverter<?> stringMessageConverter) {
|
||||||
|
this.serverMessageConverters.setStringMessageConverter(stringMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer jsonMessageConverter(HttpMessageConverter<?> jsonMessageConverter) {
|
||||||
|
this.serverMessageConverters.setJsonMessageConverter(jsonMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer xmlMessageConverter(HttpMessageConverter<?> xmlMessageConverter) {
|
||||||
|
this.serverMessageConverters.setXmlMessageConverter(xmlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer smileMessageConverter(HttpMessageConverter<?> smileMessageConverter) {
|
||||||
|
this.serverMessageConverters.setSmileMessageConverter(smileMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer cborMessageConverter(HttpMessageConverter<?> cborMessageConverter) {
|
||||||
|
this.serverMessageConverters.setCborMessageConverter(cborMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer yamlMessageConverter(HttpMessageConverter<?> yamlMessageConverter) {
|
||||||
|
this.serverMessageConverters.setYamlMessageConverter(yamlMessageConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter<?> customConverter) {
|
||||||
|
Assert.notNull(customConverter, "'customConverter' must not be null");
|
||||||
|
this.serverMessageConverters.additionalMessageConverters.add(customConverter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMessageConverterConfigurer configureServerMessageConverters(Consumer<HttpMessageConverter<?>> configurer) {
|
||||||
|
this.configurer = (this.configurer != null) ? configurer.andThen(this.configurer) : configurer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void registerDefaults(ClassLoader classLoader) {
|
||||||
|
this.resourceMessageConverters = Arrays.asList(new ResourceHttpMessageConverter(), new ResourceRegionHttpMessageConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpMessageConverter<?>> getMessageConverters() {
|
||||||
|
List<HttpMessageConverter<?>> allConverters = new ArrayList<>();
|
||||||
|
List<HttpMessageConverter<?>> partConverters = new ArrayList<>();
|
||||||
|
|
||||||
|
partConverters.addAll(this.serverMessageConverters.getCoreConverters());
|
||||||
|
partConverters.addAll(this.serverMessageConverters.getCustomConverters());
|
||||||
|
|
||||||
|
allConverters.addAll(this.serverMessageConverters.getBaseConverters());
|
||||||
|
allConverters.addAll(this.resourceMessageConverters);
|
||||||
|
allConverters.addAll(this.serverMessageConverters.getCoreConverters());
|
||||||
|
allConverters.addAll(this.serverMessageConverters.getCustomConverters());
|
||||||
|
if (!partConverters.isEmpty()) {
|
||||||
|
allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters));
|
||||||
|
}
|
||||||
|
if (this.configurer != null) {
|
||||||
|
allConverters.forEach(this.configurer);
|
||||||
|
}
|
||||||
|
return allConverters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-present 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
|
||||||
|
*
|
||||||
|
* https://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;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for building and configuring an immutable collection of {@link HttpMessageConverter}
|
||||||
|
* instances for client and server usage. You can {@link #create() create}
|
||||||
|
* a new empty instance or ask to {@link #withDefaults() register default converters},
|
||||||
|
* if available in your classpath.
|
||||||
|
*
|
||||||
|
* <p>This class offers a flexible arrangement for {@link HttpMessageConverters.Builder configuring message converters shared between}
|
||||||
|
* client and server, or {@link HttpMessageConverters.Builder#configureClient(Consumer) configuring client-specific}
|
||||||
|
* and {@link HttpMessageConverters.Builder#configureServer(Consumer) server-specific} converters.
|
||||||
|
*
|
||||||
|
* <p>The following HTTP message converters will be detected and registered if available, in order.
|
||||||
|
* For {@link #forClient() client side converters}:
|
||||||
|
* <ol>
|
||||||
|
* <li>{@link ByteArrayHttpMessageConverter}
|
||||||
|
* <li>{@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset
|
||||||
|
* <li>{@link ResourceHttpMessageConverter}, with resource streaming support disabled
|
||||||
|
* <li>a Multipart converter, using all detected and custom converters for part conversion
|
||||||
|
* <li>A JSON converter
|
||||||
|
* <li>A Smile converter
|
||||||
|
* <li>A CBOR converter
|
||||||
|
* <li>A YAML converter
|
||||||
|
* <li>An XML converter
|
||||||
|
* <li>An ProtoBuf converter
|
||||||
|
* <li>ATOM and RSS converters
|
||||||
|
* <li>All custom message converters configured with the builder
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* For {@link #forClient() client side converters}:
|
||||||
|
* <ol>
|
||||||
|
* <li>{@link ByteArrayHttpMessageConverter}
|
||||||
|
* <li>{@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset
|
||||||
|
* <li>{@link ResourceHttpMessageConverter}
|
||||||
|
* <li>{@link ResourceRegionHttpMessageConverter}
|
||||||
|
* <li>A JSON converter
|
||||||
|
* <li>A Smile converter
|
||||||
|
* <li>A CBOR converter
|
||||||
|
* <li>A YAML converter
|
||||||
|
* <li>An XML converter
|
||||||
|
* <li>An ProtoBuf converter
|
||||||
|
* <li>ATOM and RSS converters
|
||||||
|
* <li>All custom message converters configured with the builder
|
||||||
|
* <li>a Multipart converter, using all detected and custom converters for part conversion
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public interface HttpMessageConverters {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of configured message converters, tailored for HTTP client usage.
|
||||||
|
*/
|
||||||
|
Iterable<HttpMessageConverter<?>> forClient();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of configured message converters, tailored for HTTP server usage.
|
||||||
|
*/
|
||||||
|
Iterable<HttpMessageConverter<?>> forServer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a builder instance, without any message converter pre-configured.
|
||||||
|
*/
|
||||||
|
static Builder create() {
|
||||||
|
return new DefaultHttpMessageConverters.DefaultBuilder(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a builder instance with default message converters pre-configured.
|
||||||
|
*/
|
||||||
|
static Builder withDefaults() {
|
||||||
|
return new DefaultHttpMessageConverters.DefaultBuilder(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface MessageConverterConfigurer<T extends MessageConverterConfigurer<T>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default String {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting String conversion.
|
||||||
|
* @param stringMessageConverter the converter instance to use
|
||||||
|
* @see StringHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T stringMessageConverter(HttpMessageConverter<?> stringMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default Jackson 3.x JSON {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting the JSON format.
|
||||||
|
* @param jsonMessageConverter the converter instance to use
|
||||||
|
* @see org.springframework.http.converter.json.JacksonJsonHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T jsonMessageConverter(HttpMessageConverter<?> jsonMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default Jackson 3.x XML {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting the XML format.
|
||||||
|
* @param xmlMessageConverter the converter instance to use
|
||||||
|
* @see org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T xmlMessageConverter(HttpMessageConverter<?> xmlMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default Jackson 3.x Smile {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting the Smile format.
|
||||||
|
* @param smileMessageConverter the converter instance to use
|
||||||
|
* @see org.springframework.http.converter.smile.JacksonSmileHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T smileMessageConverter(HttpMessageConverter<?> smileMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default Jackson 3.x CBOR {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting the CBOR format.
|
||||||
|
* @param cborMessageConverter the converter instance to use
|
||||||
|
* @see org.springframework.http.converter.cbor.JacksonCborHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T cborMessageConverter(HttpMessageConverter<?> cborMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default Jackson 3.x Yaml {@code HttpMessageConverter}
|
||||||
|
* with any converter supporting the Yaml format.
|
||||||
|
* @param yamlMessageConverter the converter instance to use
|
||||||
|
* @see org.springframework.http.converter.yaml.JacksonYamlHttpMessageConverter
|
||||||
|
*/
|
||||||
|
T yamlMessageConverter(HttpMessageConverter<?> yamlMessageConverter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom {@code HttpMessageConverter} to the list of converters.
|
||||||
|
* @param customConverter the converter instance to add
|
||||||
|
*/
|
||||||
|
T additionalMessageConverter(HttpMessageConverter<?> customConverter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for an {@link HttpMessageConverters}.
|
||||||
|
* This builder manages the configuration of common and client/server-specific message converters.
|
||||||
|
*/
|
||||||
|
interface Builder extends MessageConverterConfigurer<Builder> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure client-specific message converters.
|
||||||
|
* If no opinion is provided here, message converters defined in this builder will be used.
|
||||||
|
*/
|
||||||
|
Builder configureClient(Consumer<ClientMessageConverterConfigurer> consumer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure server-specific message converters.
|
||||||
|
* If no opinion is provided here, message converters defined in this builder will be used.
|
||||||
|
*/
|
||||||
|
Builder configureServer(Consumer<ServerMessageConverterConfigurer> consumer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build and return the {@link HttpMessageConverters} instance configured by this builder.
|
||||||
|
*/
|
||||||
|
HttpMessageConverters build();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClientMessageConverterConfigurer extends MessageConverterConfigurer<ClientMessageConverterConfigurer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a consumer to apply to configured converter instances.
|
||||||
|
* This can be used to configure rather than replace one or more specific converters.
|
||||||
|
* @param configurer the consumer to apply
|
||||||
|
*/
|
||||||
|
ClientMessageConverterConfigurer configureClientMessageConverters(Consumer<HttpMessageConverter<?>> configurer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServerMessageConverterConfigurer extends MessageConverterConfigurer<ServerMessageConverterConfigurer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a consumer to apply to configured converter instances.
|
||||||
|
* This can be used to configure rather than replace one or more specific converters.
|
||||||
|
* @param configurer the consumer to apply
|
||||||
|
*/
|
||||||
|
ServerMessageConverterConfigurer configureServerMessageConverters(Consumer<HttpMessageConverter<?>> configurer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,434 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-present 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
|
||||||
|
*
|
||||||
|
* https://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;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.rometools.rome.feed.WireFeed;
|
||||||
|
import jakarta.json.bind.Jsonb;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import tools.jackson.databind.ObjectMapper;
|
||||||
|
import tools.jackson.dataformat.cbor.CBORMapper;
|
||||||
|
import tools.jackson.dataformat.smile.SmileMapper;
|
||||||
|
import tools.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import tools.jackson.dataformat.yaml.YAMLMapper;
|
||||||
|
|
||||||
|
import org.springframework.core.SmartClassLoader;
|
||||||
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
import org.springframework.http.HttpOutputMessage;
|
||||||
|
import org.springframework.http.converter.cbor.JacksonCborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.cbor.KotlinSerializationCborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.protobuf.KotlinSerializationProtobufHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.smile.JacksonSmileHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.yaml.JacksonYamlHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DefaultHttpMessageConverters}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
class DefaultHttpMessageConvertersTests {
|
||||||
|
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("emptyMessageConverters")
|
||||||
|
void emptyConverters(Iterable<HttpMessageConverter<?>> converters) {
|
||||||
|
assertThat(converters).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> emptyMessageConverters() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(HttpMessageConverters.create().build().forClient()),
|
||||||
|
Arguments.of(HttpMessageConverters.create().build().forServer())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void clientAndServerConvertersAreShared() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults().build();
|
||||||
|
Set<HttpMessageConverter<?>> allConverters = new HashSet<>();
|
||||||
|
converters.forClient().forEach(allConverters::add);
|
||||||
|
converters.forServer().forEach(allConverters::add);
|
||||||
|
assertThat(allConverters).hasSize(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenStringConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().stringMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("stringMessageConverter should support 'text/plain'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenJsonConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().jsonMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("jsonMessageConverter should support 'application/json'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenXmlConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().xmlMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("xmlMessageConverter should support 'text/xml'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenSmileConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().smileMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("smileMessageConverter should support 'application/x-jackson-smile'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenCborConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().cborMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("cborMessageConverter should support 'application/cbor'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failsWhenYamlConverterDoesNotSupportMediaType() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
HttpMessageConverters.create().yamlMessageConverter(new CustomHttpMessageConverter()).build())
|
||||||
|
.withMessage("yamlMessageConverter should support 'application/yaml'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ClientConvertersTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultConverters() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults().build();
|
||||||
|
assertThat(converters.forClient()).hasExactlyElementsOfTypes(ByteArrayHttpMessageConverter.class,
|
||||||
|
StringHttpMessageConverter.class, ResourceHttpMessageConverter.class,
|
||||||
|
AllEncompassingFormHttpMessageConverter.class,
|
||||||
|
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||||
|
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||||
|
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||||
|
AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipartConverterContainsOtherConverters() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults().build();
|
||||||
|
AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forClient());
|
||||||
|
|
||||||
|
assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes(
|
||||||
|
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||||
|
ResourceHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class,
|
||||||
|
JacksonSmileHttpMessageConverter.class, JacksonCborHttpMessageConverter.class,
|
||||||
|
JacksonYamlHttpMessageConverter.class, JacksonXmlHttpMessageConverter.class,
|
||||||
|
KotlinSerializationProtobufHttpMessageConverter.class, AtomFeedHttpMessageConverter.class,
|
||||||
|
RssChannelHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerCustomMessageConverter() {
|
||||||
|
var converters = HttpMessageConverters.create()
|
||||||
|
.additionalMessageConverter(new CustomHttpMessageConverter()).build();
|
||||||
|
assertThat(converters.forClient()).hasExactlyElementsOfTypes(AllEncompassingFormHttpMessageConverter.class, CustomHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerCustomConverterInMultipartConverter() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.additionalMessageConverter(new CustomHttpMessageConverter()).build();
|
||||||
|
AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forClient());
|
||||||
|
assertThat(multipartConverter.getPartConverters()).hasAtLeastOneElementOfType(CustomHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseServerSpecificConverter() {
|
||||||
|
JacksonJsonHttpMessageConverter jacksonConverter = new JacksonJsonHttpMessageConverter();
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.configureClient(configurer -> configurer.jsonMessageConverter(jacksonConverter)).build();
|
||||||
|
|
||||||
|
JacksonJsonHttpMessageConverter customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters.forClient());
|
||||||
|
assertThat(customConverter).isEqualTo(jacksonConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConfigureConverter() {
|
||||||
|
CustomHttpMessageConverter customConverter = new CustomHttpMessageConverter();
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.additionalMessageConverter(customConverter)
|
||||||
|
.configureClient(configurer -> {
|
||||||
|
configurer.configureClientMessageConverters(converter -> {
|
||||||
|
if (converter instanceof CustomHttpMessageConverter custom) {
|
||||||
|
custom.processed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).build();
|
||||||
|
|
||||||
|
assertThat(customConverter.processed).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ServerConvertersTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultConverters() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults().build();
|
||||||
|
assertThat(converters.forServer()).hasExactlyElementsOfTypes(
|
||||||
|
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||||
|
ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class,
|
||||||
|
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||||
|
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||||
|
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||||
|
AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class,
|
||||||
|
AllEncompassingFormHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multipartConverterContainsOtherConverters() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults().build();
|
||||||
|
AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forServer());
|
||||||
|
|
||||||
|
assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes(
|
||||||
|
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||||
|
ResourceHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class,
|
||||||
|
JacksonSmileHttpMessageConverter.class, JacksonCborHttpMessageConverter.class,
|
||||||
|
JacksonYamlHttpMessageConverter.class, JacksonXmlHttpMessageConverter.class,
|
||||||
|
KotlinSerializationProtobufHttpMessageConverter.class, AtomFeedHttpMessageConverter.class,
|
||||||
|
RssChannelHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerCustomMessageConverter() {
|
||||||
|
var converters = HttpMessageConverters.create()
|
||||||
|
.additionalMessageConverter(new CustomHttpMessageConverter()).build();
|
||||||
|
assertThat(converters.forServer()).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerCustomConverterInMultipartConverter() {
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.additionalMessageConverter(new CustomHttpMessageConverter()).build();
|
||||||
|
AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forServer());
|
||||||
|
assertThat(multipartConverter.getPartConverters()).hasAtLeastOneElementOfType(CustomHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUseServerSpecificConverter() {
|
||||||
|
JacksonJsonHttpMessageConverter jacksonConverter = new JacksonJsonHttpMessageConverter();
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.configureServer(configurer -> configurer.jsonMessageConverter(jacksonConverter)).build();
|
||||||
|
|
||||||
|
JacksonJsonHttpMessageConverter customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters.forServer());
|
||||||
|
assertThat(customConverter).isEqualTo(jacksonConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConfigureConverter() {
|
||||||
|
CustomHttpMessageConverter customConverter = new CustomHttpMessageConverter();
|
||||||
|
var converters = HttpMessageConverters.withDefaults()
|
||||||
|
.additionalMessageConverter(customConverter)
|
||||||
|
.configureServer(configurer -> {
|
||||||
|
configurer.configureServerMessageConverters(converter -> {
|
||||||
|
if (converter instanceof CustomHttpMessageConverter custom) {
|
||||||
|
custom.processed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).build();
|
||||||
|
|
||||||
|
assertThat(customConverter.processed).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ClasspathDetectionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonUsesJackson2WhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(ObjectMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(MappingJackson2HttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonJsonHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonUsesGsonWhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(ObjectMapper.class, com.fasterxml.jackson.databind.ObjectMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(GsonHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonJsonHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonUsesJsonbWhenJacksonAndGsonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(ObjectMapper.class, com.fasterxml.jackson.databind.ObjectMapper.class, Gson.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(JsonbHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonJsonHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class,
|
||||||
|
GsonHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonUsesKotlinWhenOthersNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(ObjectMapper.class, com.fasterxml.jackson.databind.ObjectMapper.class, Gson.class, Jsonb.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(KotlinSerializationJsonHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonJsonHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class,
|
||||||
|
GsonHttpMessageConverter.class, JsonbHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void xmlUsesJackson2WhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(XmlMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(MappingJackson2XmlHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonXmlHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void xmlUsesJaxbWhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(XmlMapper.class, com.fasterxml.jackson.dataformat.xml.XmlMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(Jaxb2RootElementHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonXmlHttpMessageConverter.class, MappingJackson2XmlHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smileUsesJackson2WhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(SmileMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(MappingJackson2SmileHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonSmileHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cborUsesJackson2WhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(CBORMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(MappingJackson2CborHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonCborHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cborUsesKotlinWhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(CBORMapper.class, com.fasterxml.jackson.dataformat.cbor.CBORFactory.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(KotlinSerializationCborHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonCborHttpMessageConverter.class, MappingJackson2CborHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void yamlUsesJackson2WhenJacksonNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(YAMLMapper.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).hasAtLeastOneElementOfType(MappingJackson2YamlHttpMessageConverter.class)
|
||||||
|
.doesNotHaveAnyElementsOfTypes(JacksonYamlHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void atomAndRssNotConfiguredWhenRomeNotPresent() {
|
||||||
|
var classLoader = new FilteredClassLoader(WireFeed.class);
|
||||||
|
var converters = new DefaultHttpMessageConverters.DefaultBuilder(true, classLoader).build();
|
||||||
|
assertThat(converters.forServer()).doesNotHaveAnyElementsOfTypes(AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T findMessageConverter(Class<T> converterType, Iterable<HttpMessageConverter<?>> converters) {
|
||||||
|
return (T) StreamSupport
|
||||||
|
.stream(converters.spliterator(), false)
|
||||||
|
.filter(converter -> converter.getClass().equals(converterType))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class FilteredClassLoader extends URLClassLoader implements SmartClassLoader {
|
||||||
|
|
||||||
|
private final Collection<Class<?>> hiddenClasses;
|
||||||
|
|
||||||
|
public FilteredClassLoader(Class<?>... hiddenClasses) {
|
||||||
|
this(java.util.Arrays.asList(hiddenClasses));
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredClassLoader(Collection<Class<?>> hiddenClasses) {
|
||||||
|
super(new URL[0], FilteredClassLoader.class.getClassLoader());
|
||||||
|
this.hiddenClasses = hiddenClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
for (Class<?> hiddenClass : this.hiddenClasses) {
|
||||||
|
if (hiddenClass.getName().equals(name)) {
|
||||||
|
throw new ClassNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
|
||||||
|
|
||||||
|
boolean processed = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supports(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue