diff --git a/spring-core/src/main/java/org/springframework/core/codec/StringEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java similarity index 72% rename from spring-core/src/main/java/org/springframework/core/codec/StringEncoder.java rename to spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java index 8e8b28c23e..03f61e1de6 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/StringEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java @@ -16,6 +16,8 @@ package org.springframework.core.codec; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -28,18 +30,19 @@ import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.util.MimeType; /** - * Encode from a String stream to a bytes stream. + * Encode from a CharSequence stream to a bytes stream. * * @author Sebastien Deleuze + * @author Arjen Poutsma * @since 5.0 * @see StringDecoder */ -public class StringEncoder extends AbstractEncoder { +public class CharSequenceEncoder extends AbstractEncoder { public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - public StringEncoder() { + public CharSequenceEncoder() { super(new MimeType("text", "plain", DEFAULT_CHARSET)); } @@ -47,11 +50,11 @@ public class StringEncoder extends AbstractEncoder { @Override public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) { Class clazz = elementType.getRawClass(); - return (super.canEncode(elementType, mimeType, hints) && String.class.equals(clazz)); + return (super.canEncode(elementType, mimeType, hints) && CharSequence.class.isAssignableFrom(clazz)); } @Override - public Flux encode(Publisher inputStream, + public Flux encode(Publisher inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Object... hints) { @@ -62,11 +65,10 @@ public class StringEncoder extends AbstractEncoder { else { charset = DEFAULT_CHARSET; } - return Flux.from(inputStream).map(s -> { - byte[] bytes = s.getBytes(charset); - DataBuffer dataBuffer = bufferFactory.allocateBuffer(bytes.length); - dataBuffer.write(bytes); - return dataBuffer; + return Flux.from(inputStream).map(charSequence -> { + CharBuffer charBuffer = CharBuffer.wrap(charSequence); + ByteBuffer byteBuffer = charset.encode(charBuffer); + return bufferFactory.wrap(byteBuffer); }); } diff --git a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java index aaddc199f8..c56141fbd8 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java @@ -45,7 +45,7 @@ import org.springframework.util.MimeTypeUtils; * @author Arjen Poutsma * @author Mark Paluch * @since 5.0 - * @see StringEncoder + * @see CharSequenceEncoder */ public class StringDecoder extends AbstractDecoder { diff --git a/spring-core/src/test/java/org/springframework/core/codec/StringEncoderTests.java b/spring-core/src/test/java/org/springframework/core/codec/CharSequenceEncoderTests.java similarity index 60% rename from spring-core/src/test/java/org/springframework/core/codec/StringEncoderTests.java rename to spring-core/src/test/java/org/springframework/core/codec/CharSequenceEncoderTests.java index 879b78a4bf..b0204309e9 100644 --- a/spring-core/src/test/java/org/springframework/core/codec/StringEncoderTests.java +++ b/spring-core/src/test/java/org/springframework/core/codec/CharSequenceEncoderTests.java @@ -27,6 +27,7 @@ import reactor.test.TestSubscriber; import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.support.DataBufferUtils; import org.springframework.util.MimeTypeUtils; @@ -37,35 +38,46 @@ import static org.junit.Assert.assertTrue; * @author Sebastien Deleuze */ @RunWith(Parameterized.class) -public class StringEncoderTests extends AbstractDataBufferAllocatingTestCase { +public class CharSequenceEncoderTests extends AbstractDataBufferAllocatingTestCase { - private StringEncoder encoder; + private CharSequenceEncoder encoder; @Before public void createEncoder() { - this.encoder = new StringEncoder(); + this.encoder = new CharSequenceEncoder(); } @Test public void canWrite() { assertTrue(this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_PLAIN)); + assertTrue(this.encoder.canEncode(ResolvableType.forClass(StringBuilder.class), MimeTypeUtils.TEXT_PLAIN)); + assertTrue(this.encoder.canEncode(ResolvableType.forClass(StringBuffer.class), MimeTypeUtils.TEXT_PLAIN)); assertFalse(this.encoder.canEncode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN)); assertFalse(this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.APPLICATION_JSON)); } @Test - public void write() throws InterruptedException { - Flux output = Flux.from( - this.encoder.encode(Flux.just("foo"), this.bufferFactory, null, null)) - .map(chunk -> { - byte[] b = new byte[chunk.readableByteCount()]; - chunk.read(b); - DataBufferUtils.release(chunk); - return new String(b, StandardCharsets.UTF_8); - }); + public void writeString() throws InterruptedException { + Flux stringFlux = Flux.just("foo"); + Flux output = Flux.from( + this.encoder.encode(stringFlux, this.bufferFactory, null, null)); TestSubscriber .subscribe(output) - .assertValues("foo"); + .assertNoError() + .assertComplete() + .assertValuesWith(stringConsumer("foo")); + } + + @Test + public void writeStringBuilder() throws InterruptedException { + Flux stringBuilderFlux = Flux.just(new StringBuilder("foo")); + Flux output = Flux.from( + this.encoder.encode(stringBuilderFlux, this.bufferFactory, null, null)); + TestSubscriber + .subscribe(output) + .assertNoError() + .assertComplete() + .assertValuesWith(stringConsumer("foo")); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java index d8bc0dcc96..7cbe119980 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java @@ -29,10 +29,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.codec.ByteBufferDecoder; import org.springframework.core.codec.ByteBufferEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.codec.Encoder; import org.springframework.core.codec.ResourceDecoder; import org.springframework.core.codec.StringDecoder; -import org.springframework.core.codec.StringEncoder; import org.springframework.core.convert.converter.Converter; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; @@ -368,7 +368,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware { protected final void addDefaultHttpMessageWriters(List> writers) { List> sseDataEncoders = new ArrayList<>(); writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); - writers.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + writers.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); writers.add(new ResourceHttpMessageWriter()); if (jaxb2Present) { writers.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder())); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java index 91a1bc2e23..d26a84a991 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java @@ -29,7 +29,7 @@ import reactor.test.TestSubscriber; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.codec.StringEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpMethod; @@ -61,6 +61,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; + /** * Test the effect of exceptions at different stages of request processing by * checking the error signals on the completion publisher. @@ -197,7 +198,7 @@ public class DispatcherHandlerErrorTests { @Bean public ResponseBodyResultHandler resultHandler() { return new ResponseBodyResultHandler( - Collections.singletonList(new EncoderHttpMessageWriter<>(new StringEncoder())), + Collections.singletonList(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())), new HeaderContentTypeResolver()); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java index 83bde44174..3a4ea9215c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.reactive.config; import java.net.URI; @@ -34,8 +35,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.codec.StringDecoder; -import org.springframework.core.codec.StringEncoder; import org.springframework.core.convert.ConversionService; import org.springframework.core.io.Resource; import org.springframework.http.HttpMethod; @@ -68,16 +69,8 @@ import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM; -import static org.springframework.http.MediaType.APPLICATION_XML; -import static org.springframework.http.MediaType.IMAGE_PNG; -import static org.springframework.http.MediaType.TEXT_PLAIN; +import static org.junit.Assert.*; +import static org.springframework.http.MediaType.*; /** * Unit tests for {@link WebReactiveConfiguration}. @@ -307,7 +300,7 @@ public class WebReactiveConfigurationTests { @Override protected void configureMessageWriters(List> messageWriters) { - messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); } @Override diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java index f01b6b51e0..37f634f738 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java @@ -30,7 +30,6 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,7 +39,7 @@ import rx.Observable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.ByteBufferEncoder; -import org.springframework.core.codec.StringEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.buffer.support.DataBufferTestUtils; @@ -176,7 +175,7 @@ public class MessageWriterResultHandlerTests { if (ObjectUtils.isEmpty(writers)) { writerList = new ArrayList<>(); writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); - writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); writerList.add(new ResourceHttpMessageWriter()); writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder())); writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder())); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java index 32abc9d5dd..e8bfd61395 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java @@ -26,7 +26,7 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.core.codec.ByteBufferEncoder; -import org.springframework.core.codec.StringEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.codec.json.JacksonJsonEncoder; @@ -86,7 +86,7 @@ public class ResponseBodyResultHandlerTests { if (ObjectUtils.isEmpty(writers)) { writerList = new ArrayList<>(); writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); - writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); writerList.add(new ResourceHttpMessageWriter()); writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder())); writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder())); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java index 0034b3050c..43198489dc 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java @@ -33,7 +33,7 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.ByteBufferEncoder; -import org.springframework.core.codec.StringEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.io.buffer.support.DataBufferTestUtils; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -91,7 +91,7 @@ public class ResponseEntityResultHandlerTests { if (ObjectUtils.isEmpty(writers)) { writerList = new ArrayList<>(); writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); - writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); writerList.add(new ResourceHttpMessageWriter()); writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder())); writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder())); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java index 1ff2ce13f4..22cdda24c4 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.web.reactive.result.view; +package org.springframework.web.reactive.result.view; import java.net.URI; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @@ -32,7 +31,7 @@ import org.junit.Test; import reactor.test.TestSubscriber; import org.springframework.core.MethodParameter; -import org.springframework.core.codec.StringEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.io.buffer.support.DataBufferTestUtils; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -51,10 +50,7 @@ import org.springframework.web.server.session.DefaultWebSessionManager; import org.springframework.web.server.session.WebSessionManager; import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** @@ -123,7 +119,7 @@ public class HttpMessageWriterViewTests { @Test public void extractObjectMultipleMatchesNotSupported() throws Exception { - HttpMessageWriterView view = new HttpMessageWriterView(new StringEncoder()); + HttpMessageWriterView view = new HttpMessageWriterView(new CharSequenceEncoder()); view.setModelKeys(new HashSet<>(Arrays.asList("foo1", "foo2"))); this.model.addAttribute("foo1", "bar1"); this.model.addAttribute("foo2", "bar2"); diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java index 8831ee949c..2e786cc356 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java @@ -30,9 +30,9 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.codec.ByteBufferDecoder; import org.springframework.core.codec.ByteBufferEncoder; +import org.springframework.core.codec.CharSequenceEncoder; import org.springframework.core.codec.ResourceDecoder; import org.springframework.core.codec.StringDecoder; -import org.springframework.core.codec.StringEncoder; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpConnector; @@ -105,7 +105,7 @@ public final class WebClient { * Register by default the following Encoders and Decoders: *
    *
  • {@link ByteBufferEncoder} / {@link ByteBufferDecoder}
  • - *
  • {@link StringEncoder} / {@link StringDecoder}
  • + *
  • {@link CharSequenceEncoder} / {@link StringDecoder}
  • *
  • {@link Jaxb2Encoder} / {@link Jaxb2Decoder}
  • *
  • {@link JacksonJsonEncoder} / {@link JacksonJsonDecoder}
  • *
@@ -138,7 +138,7 @@ public final class WebClient { */ protected final void addDefaultHttpMessageWriters(List> messageWriters) { messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); - messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); messageWriters.add(new ResourceHttpMessageWriter()); if (jaxb2Present) { messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));