Add ClientCodecConfigurer
Issue: SPR-15247
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.codec;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ClientCodecConfigurer extends AbstractCodecConfigurer {
|
||||
|
||||
|
||||
public ClientCodecConfigurer() {
|
||||
super(new ClientDefaultCodecConfigurer());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientDefaultCodecConfigurer defaultCodec() {
|
||||
return (ClientDefaultCodecConfigurer) super.defaultCodec();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void addDefaultTypedWriter(List<HttpMessageWriter<?>> result) {
|
||||
super.addDefaultTypedWriter(result);
|
||||
defaultCodec().addWriterTo(result, FormHttpMessageWriter::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDefaultObjectReaders(List<HttpMessageReader<?>> result) {
|
||||
super.addDefaultObjectReaders(result);
|
||||
defaultCodec().addServerSentEventReaderTo(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of {@code DefaultCodecConfigurer} with extra client options.
|
||||
*/
|
||||
public static class ClientDefaultCodecConfigurer extends DefaultCodecConfigurer {
|
||||
|
||||
/**
|
||||
* Configure the {@code Decoder} to use for Server-Sent Events.
|
||||
* <p>By default the {@link #jackson2Decoder} override is used for SSE.
|
||||
* @param decoder the decoder to use
|
||||
*/
|
||||
public void serverSentEventDecoder(Decoder<?> decoder) {
|
||||
HttpMessageReader<?> reader = new ServerSentEventHttpMessageReader(decoder);
|
||||
getReaders().put(ServerSentEventHttpMessageReader.class, reader);
|
||||
}
|
||||
|
||||
|
||||
// Internal methods for building a list of default readers or writers...
|
||||
|
||||
private void addServerSentEventReaderTo(List<HttpMessageReader<?>> result) {
|
||||
addReaderTo(result, () -> findReader(ServerSentEventHttpMessageReader.class, () -> {
|
||||
Decoder<?> decoder = null;
|
||||
if (jackson2Present) {
|
||||
decoder = findDecoderReader(
|
||||
Jackson2JsonDecoder.class, Jackson2JsonDecoder::new).getDecoder();
|
||||
}
|
||||
return new ServerSentEventHttpMessageReader(decoder);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.codec;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.codec.ByteArrayDecoder;
|
||||
import org.springframework.core.codec.ByteArrayEncoder;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.DataBufferDecoder;
|
||||
import org.springframework.core.codec.DataBufferEncoder;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.ResourceDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ClientCodecConfigurer}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ClientCodecConfigurerTests {
|
||||
|
||||
private final ClientCodecConfigurer configurer = new ClientCodecConfigurer();
|
||||
|
||||
private final AtomicInteger index = new AtomicInteger(0);
|
||||
|
||||
|
||||
@Test
|
||||
public void defaultReaders() throws Exception {
|
||||
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
|
||||
assertEquals(9, readers.size());
|
||||
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertEquals(ResourceDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertStringDecoder(getNextDecoder(readers), true);
|
||||
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
|
||||
assertSseReader(readers);
|
||||
assertStringDecoder(getNextDecoder(readers), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultWriters() throws Exception {
|
||||
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
|
||||
assertEquals(9, writers.size());
|
||||
assertEquals(ByteArrayEncoder.class, getNextEncoder(writers).getClass());
|
||||
assertEquals(ByteBufferEncoder.class, getNextEncoder(writers).getClass());
|
||||
assertEquals(DataBufferEncoder.class, getNextEncoder(writers).getClass());
|
||||
assertEquals(ResourceHttpMessageWriter.class, writers.get(index.getAndIncrement()).getClass());
|
||||
assertStringEncoder(getNextEncoder(writers), true);
|
||||
assertEquals(FormHttpMessageWriter.class, writers.get(this.index.getAndIncrement()).getClass());
|
||||
assertEquals(Jaxb2XmlEncoder.class, getNextEncoder(writers).getClass());
|
||||
assertEquals(Jackson2JsonEncoder.class, getNextEncoder(writers).getClass());
|
||||
assertStringEncoder(getNextEncoder(writers), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jackson2EncoderOverride() throws Exception {
|
||||
|
||||
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
|
||||
this.configurer.defaultCodec().jackson2Decoder(decoder);
|
||||
|
||||
assertSame(decoder, this.configurer.getReaders().stream()
|
||||
.filter(reader -> ServerSentEventHttpMessageReader.class.equals(reader.getClass()))
|
||||
.map(reader -> (ServerSentEventHttpMessageReader) reader)
|
||||
.findFirst()
|
||||
.map(ServerSentEventHttpMessageReader::getDecoder)
|
||||
.filter(e -> e == decoder).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
private Decoder<?> getNextDecoder(List<HttpMessageReader<?>> readers) {
|
||||
HttpMessageReader<?> reader = readers.get(this.index.getAndIncrement());
|
||||
assertEquals(DecoderHttpMessageReader.class, reader.getClass());
|
||||
return ((DecoderHttpMessageReader) reader).getDecoder();
|
||||
}
|
||||
|
||||
private Encoder<?> getNextEncoder(List<HttpMessageWriter<?>> writers) {
|
||||
HttpMessageWriter<?> writer = writers.get(this.index.getAndIncrement());
|
||||
assertEquals(EncoderHttpMessageWriter.class, writer.getClass());
|
||||
return ((EncoderHttpMessageWriter) writer).getEncoder();
|
||||
}
|
||||
|
||||
private void assertStringDecoder(Decoder<?> decoder, boolean textOnly) {
|
||||
assertEquals(StringDecoder.class, decoder.getClass());
|
||||
assertTrue(decoder.canDecode(forClass(String.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
assertEquals(!textOnly, decoder.canDecode(forClass(String.class), MediaType.TEXT_EVENT_STREAM));
|
||||
}
|
||||
|
||||
private void assertStringEncoder(Encoder<?> encoder, boolean textOnly) {
|
||||
assertEquals(CharSequenceEncoder.class, encoder.getClass());
|
||||
assertTrue(encoder.canEncode(forClass(String.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
assertEquals(!textOnly, encoder.canEncode(forClass(String.class), MediaType.TEXT_EVENT_STREAM));
|
||||
}
|
||||
|
||||
private void assertSseReader(List<HttpMessageReader<?>> readers) {
|
||||
HttpMessageReader<?> reader = readers.get(this.index.getAndIncrement());
|
||||
assertEquals(ServerSentEventHttpMessageReader.class, reader.getClass());
|
||||
Decoder<?> decoder = ((ServerSentEventHttpMessageReader) reader).getDecoder();
|
||||
assertNotNull(decoder);
|
||||
assertEquals(Jackson2JsonDecoder.class, decoder.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,27 +23,14 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.codec.ByteArrayDecoder;
|
||||
import org.springframework.core.codec.ByteArrayEncoder;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.codec.ClientCodecConfigurer;
|
||||
import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.FormHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ExchangeStrategies.Builder}.
|
||||
@@ -53,62 +40,15 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Builder {
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder",
|
||||
DefaultExchangeStrategiesBuilder.class.getClassLoader());
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>();
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
|
||||
|
||||
|
||||
public void defaultConfiguration() {
|
||||
defaultReaders();
|
||||
defaultWriters();
|
||||
}
|
||||
|
||||
private void defaultReaders() {
|
||||
messageReader(new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
||||
messageReader(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
messageReader(new DecoderHttpMessageReader<>(StringDecoder.textPlainOnly(false)));
|
||||
if (jaxb2Present) {
|
||||
messageReader(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
messageReader(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
|
||||
}
|
||||
messageReader(new ServerSentEventHttpMessageReader(getSseDecoder()));
|
||||
messageReader(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(false)));
|
||||
}
|
||||
|
||||
private Decoder<?> getSseDecoder() {
|
||||
if (jackson2Present) {
|
||||
return new Jackson2JsonDecoder();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultWriters() {
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteArrayEncoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
messageWriter(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
|
||||
messageWriter(new ResourceHttpMessageWriter());
|
||||
messageWriter(new FormHttpMessageWriter());
|
||||
if (jaxb2Present) {
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
messageWriter(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
|
||||
}
|
||||
ClientCodecConfigurer configurer = new ClientCodecConfigurer();
|
||||
configurer.getReaders().forEach(this::messageReader);
|
||||
configurer.getWriters().forEach(this::messageWriter);
|
||||
}
|
||||
|
||||
public void applicationContext(ApplicationContext applicationContext) {
|
||||
|
||||
Reference in New Issue
Block a user