Skip to content

Commit b0d7625

Browse files
poutsmarstoyanchev
authored andcommitted
Reactor StringEncoder into CharSequenceEncoder
This commit refactors the StringEncoder to a CharSequenceEncoder, in order to support StringBuilders, Groovy GStrings, etc. Issue: https://github.com/spring-projects/spring-reactive/issues/120
1 parent 0f6505e commit b0d7625

File tree

11 files changed

+61
-58
lines changed

11 files changed

+61
-58
lines changed

spring-core/src/main/java/org/springframework/core/codec/StringEncoder.java renamed to spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.core.codec;
1818

19+
import java.nio.ByteBuffer;
20+
import java.nio.CharBuffer;
1921
import java.nio.charset.Charset;
2022
import java.nio.charset.StandardCharsets;
2123

@@ -28,30 +30,31 @@
2830
import org.springframework.util.MimeType;
2931

3032
/**
31-
* Encode from a String stream to a bytes stream.
33+
* Encode from a CharSequence stream to a bytes stream.
3234
*
3335
* @author Sebastien Deleuze
36+
* @author Arjen Poutsma
3437
* @since 5.0
3538
* @see StringDecoder
3639
*/
37-
public class StringEncoder extends AbstractEncoder<String> {
40+
public class CharSequenceEncoder extends AbstractEncoder<CharSequence> {
3841

3942
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
4043

4144

42-
public StringEncoder() {
45+
public CharSequenceEncoder() {
4346
super(new MimeType("text", "plain", DEFAULT_CHARSET));
4447
}
4548

4649

4750
@Override
4851
public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
4952
Class<?> clazz = elementType.getRawClass();
50-
return (super.canEncode(elementType, mimeType, hints) && String.class.equals(clazz));
53+
return (super.canEncode(elementType, mimeType, hints) && CharSequence.class.isAssignableFrom(clazz));
5154
}
5255

5356
@Override
54-
public Flux<DataBuffer> encode(Publisher<? extends String> inputStream,
57+
public Flux<DataBuffer> encode(Publisher<? extends CharSequence> inputStream,
5558
DataBufferFactory bufferFactory, ResolvableType elementType,
5659
MimeType mimeType, Object... hints) {
5760

@@ -62,11 +65,10 @@ public Flux<DataBuffer> encode(Publisher<? extends String> inputStream,
6265
else {
6366
charset = DEFAULT_CHARSET;
6467
}
65-
return Flux.from(inputStream).map(s -> {
66-
byte[] bytes = s.getBytes(charset);
67-
DataBuffer dataBuffer = bufferFactory.allocateBuffer(bytes.length);
68-
dataBuffer.write(bytes);
69-
return dataBuffer;
68+
return Flux.from(inputStream).map(charSequence -> {
69+
CharBuffer charBuffer = CharBuffer.wrap(charSequence);
70+
ByteBuffer byteBuffer = charset.encode(charBuffer);
71+
return bufferFactory.wrap(byteBuffer);
7072
});
7173
}
7274

spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* @author Arjen Poutsma
4646
* @author Mark Paluch
4747
* @since 5.0
48-
* @see StringEncoder
48+
* @see CharSequenceEncoder
4949
*/
5050
public class StringDecoder extends AbstractDecoder<String> {
5151

spring-core/src/test/java/org/springframework/core/codec/StringEncoderTests.java renamed to spring-core/src/test/java/org/springframework/core/codec/CharSequenceEncoderTests.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.springframework.core.ResolvableType;
2929
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
30+
import org.springframework.core.io.buffer.DataBuffer;
3031
import org.springframework.core.io.buffer.support.DataBufferUtils;
3132
import org.springframework.util.MimeTypeUtils;
3233

@@ -37,35 +38,46 @@
3738
* @author Sebastien Deleuze
3839
*/
3940
@RunWith(Parameterized.class)
40-
public class StringEncoderTests extends AbstractDataBufferAllocatingTestCase {
41+
public class CharSequenceEncoderTests extends AbstractDataBufferAllocatingTestCase {
4142

42-
private StringEncoder encoder;
43+
private CharSequenceEncoder encoder;
4344

4445
@Before
4546
public void createEncoder() {
46-
this.encoder = new StringEncoder();
47+
this.encoder = new CharSequenceEncoder();
4748
}
4849

4950
@Test
5051
public void canWrite() {
5152
assertTrue(this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_PLAIN));
53+
assertTrue(this.encoder.canEncode(ResolvableType.forClass(StringBuilder.class), MimeTypeUtils.TEXT_PLAIN));
54+
assertTrue(this.encoder.canEncode(ResolvableType.forClass(StringBuffer.class), MimeTypeUtils.TEXT_PLAIN));
5255
assertFalse(this.encoder.canEncode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN));
5356
assertFalse(this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.APPLICATION_JSON));
5457
}
5558

5659
@Test
57-
public void write() throws InterruptedException {
58-
Flux<String> output = Flux.from(
59-
this.encoder.encode(Flux.just("foo"), this.bufferFactory, null, null))
60-
.map(chunk -> {
61-
byte[] b = new byte[chunk.readableByteCount()];
62-
chunk.read(b);
63-
DataBufferUtils.release(chunk);
64-
return new String(b, StandardCharsets.UTF_8);
65-
});
60+
public void writeString() throws InterruptedException {
61+
Flux<String> stringFlux = Flux.just("foo");
62+
Flux<DataBuffer> output = Flux.from(
63+
this.encoder.encode(stringFlux, this.bufferFactory, null, null));
6664
TestSubscriber
6765
.subscribe(output)
68-
.assertValues("foo");
66+
.assertNoError()
67+
.assertComplete()
68+
.assertValuesWith(stringConsumer("foo"));
69+
}
70+
71+
@Test
72+
public void writeStringBuilder() throws InterruptedException {
73+
Flux<StringBuilder> stringBuilderFlux = Flux.just(new StringBuilder("foo"));
74+
Flux<DataBuffer> output = Flux.from(
75+
this.encoder.encode(stringBuilderFlux, this.bufferFactory, null, null));
76+
TestSubscriber
77+
.subscribe(output)
78+
.assertNoError()
79+
.assertComplete()
80+
.assertValuesWith(stringConsumer("foo"));
6981
}
7082

7183
}

spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
import org.springframework.context.annotation.Configuration;
3030
import org.springframework.core.codec.ByteBufferDecoder;
3131
import org.springframework.core.codec.ByteBufferEncoder;
32+
import org.springframework.core.codec.CharSequenceEncoder;
3233
import org.springframework.core.codec.Encoder;
3334
import org.springframework.core.codec.ResourceDecoder;
3435
import org.springframework.core.codec.StringDecoder;
35-
import org.springframework.core.codec.StringEncoder;
3636
import org.springframework.core.convert.converter.Converter;
3737
import org.springframework.format.Formatter;
3838
import org.springframework.format.FormatterRegistry;
@@ -368,7 +368,7 @@ protected void configureMessageWriters(List<HttpMessageWriter<?>> messageWriters
368368
protected final void addDefaultHttpMessageWriters(List<HttpMessageWriter<?>> writers) {
369369
List<Encoder<?>> sseDataEncoders = new ArrayList<>();
370370
writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
371-
writers.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
371+
writers.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
372372
writers.add(new ResourceHttpMessageWriter());
373373
if (jaxb2Present) {
374374
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));

spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3030
import org.springframework.context.annotation.Bean;
3131
import org.springframework.context.annotation.Configuration;
32-
import org.springframework.core.codec.StringEncoder;
32+
import org.springframework.core.codec.CharSequenceEncoder;
3333
import org.springframework.core.io.buffer.DataBuffer;
3434
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
3535
import org.springframework.http.HttpMethod;
@@ -61,6 +61,7 @@
6161
import static org.junit.Assert.assertSame;
6262
import static org.junit.Assert.assertThat;
6363

64+
6465
/**
6566
* Test the effect of exceptions at different stages of request processing by
6667
* checking the error signals on the completion publisher.
@@ -197,7 +198,7 @@ public RequestMappingHandlerAdapter handlerAdapter() {
197198
@Bean
198199
public ResponseBodyResultHandler resultHandler() {
199200
return new ResponseBodyResultHandler(
200-
Collections.singletonList(new EncoderHttpMessageWriter<>(new StringEncoder())),
201+
Collections.singletonList(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())),
201202
new HeaderContentTypeResolver());
202203
}
203204

spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.web.reactive.config;
1718

1819
import java.net.URI;
@@ -34,8 +35,8 @@
3435
import org.springframework.context.annotation.Configuration;
3536
import org.springframework.core.Ordered;
3637
import org.springframework.core.ResolvableType;
38+
import org.springframework.core.codec.CharSequenceEncoder;
3739
import org.springframework.core.codec.StringDecoder;
38-
import org.springframework.core.codec.StringEncoder;
3940
import org.springframework.core.convert.ConversionService;
4041
import org.springframework.core.io.Resource;
4142
import org.springframework.http.HttpMethod;
@@ -68,16 +69,8 @@
6869
import org.springframework.web.server.adapter.DefaultServerWebExchange;
6970
import org.springframework.web.server.session.MockWebSessionManager;
7071

71-
import static org.junit.Assert.assertEquals;
72-
import static org.junit.Assert.assertFalse;
73-
import static org.junit.Assert.assertNotNull;
74-
import static org.junit.Assert.assertSame;
75-
import static org.junit.Assert.assertTrue;
76-
import static org.springframework.http.MediaType.APPLICATION_JSON;
77-
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
78-
import static org.springframework.http.MediaType.APPLICATION_XML;
79-
import static org.springframework.http.MediaType.IMAGE_PNG;
80-
import static org.springframework.http.MediaType.TEXT_PLAIN;
72+
import static org.junit.Assert.*;
73+
import static org.springframework.http.MediaType.*;
8174

8275
/**
8376
* Unit tests for {@link WebReactiveConfiguration}.
@@ -307,7 +300,7 @@ protected void configureMessageReaders(List<HttpMessageReader<?>> messageReaders
307300

308301
@Override
309302
protected void configureMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
310-
messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
303+
messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
311304
}
312305

313306
@Override

spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import com.fasterxml.jackson.annotation.JsonTypeInfo;
3131
import com.fasterxml.jackson.annotation.JsonTypeName;
3232
import org.junit.Before;
33-
import org.junit.Ignore;
3433
import org.junit.Test;
3534
import reactor.core.publisher.Flux;
3635
import reactor.core.publisher.Mono;
@@ -40,7 +39,7 @@
4039
import org.springframework.core.MethodParameter;
4140
import org.springframework.core.ResolvableType;
4241
import org.springframework.core.codec.ByteBufferEncoder;
43-
import org.springframework.core.codec.StringEncoder;
42+
import org.springframework.core.codec.CharSequenceEncoder;
4443
import org.springframework.core.io.ClassPathResource;
4544
import org.springframework.core.io.Resource;
4645
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
@@ -176,7 +175,7 @@ private AbstractMessageWriterResultHandler createResultHandler(HttpMessageWriter
176175
if (ObjectUtils.isEmpty(writers)) {
177176
writerList = new ArrayList<>();
178177
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
179-
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
178+
writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
180179
writerList.add(new ResourceHttpMessageWriter());
181180
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
182181
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));

spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import reactor.core.publisher.Mono;
2727

2828
import org.springframework.core.codec.ByteBufferEncoder;
29-
import org.springframework.core.codec.StringEncoder;
29+
import org.springframework.core.codec.CharSequenceEncoder;
3030
import org.springframework.http.HttpMethod;
3131
import org.springframework.http.ResponseEntity;
3232
import org.springframework.http.codec.json.JacksonJsonEncoder;
@@ -86,7 +86,7 @@ private ResponseBodyResultHandler createHandler(HttpMessageWriter<?>... writers)
8686
if (ObjectUtils.isEmpty(writers)) {
8787
writerList = new ArrayList<>();
8888
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
89-
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
89+
writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
9090
writerList.add(new ResourceHttpMessageWriter());
9191
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
9292
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));

spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.springframework.core.MethodParameter;
3434
import org.springframework.core.ResolvableType;
3535
import org.springframework.core.codec.ByteBufferEncoder;
36-
import org.springframework.core.codec.StringEncoder;
36+
import org.springframework.core.codec.CharSequenceEncoder;
3737
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
3838
import org.springframework.http.HttpMethod;
3939
import org.springframework.http.HttpStatus;
@@ -91,7 +91,7 @@ private ResponseEntityResultHandler createHandler(HttpMessageWriter<?>... writer
9191
if (ObjectUtils.isEmpty(writers)) {
9292
writerList = new ArrayList<>();
9393
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
94-
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
94+
writerList.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
9595
writerList.add(new ResourceHttpMessageWriter());
9696
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
9797
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));

spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.web.reactive.result.view;
1716

17+
package org.springframework.web.reactive.result.view;
1818

1919
import java.net.URI;
20-
import java.nio.charset.Charset;
2120
import java.nio.charset.StandardCharsets;
2221
import java.util.Arrays;
2322
import java.util.Collections;
@@ -32,7 +31,7 @@
3231
import reactor.test.TestSubscriber;
3332

3433
import org.springframework.core.MethodParameter;
35-
import org.springframework.core.codec.StringEncoder;
34+
import org.springframework.core.codec.CharSequenceEncoder;
3635
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
3736
import org.springframework.http.HttpMethod;
3837
import org.springframework.http.MediaType;
@@ -51,10 +50,7 @@
5150
import org.springframework.web.server.session.WebSessionManager;
5251

5352
import static junit.framework.TestCase.assertTrue;
54-
import static org.junit.Assert.assertEquals;
55-
import static org.junit.Assert.assertNotNull;
56-
import static org.junit.Assert.assertNull;
57-
import static org.junit.Assert.fail;
53+
import static org.junit.Assert.*;
5854

5955

6056
/**
@@ -123,7 +119,7 @@ public void extractObjectMultipleMatches() throws Exception {
123119

124120
@Test
125121
public void extractObjectMultipleMatchesNotSupported() throws Exception {
126-
HttpMessageWriterView view = new HttpMessageWriterView(new StringEncoder());
122+
HttpMessageWriterView view = new HttpMessageWriterView(new CharSequenceEncoder());
127123
view.setModelKeys(new HashSet<>(Arrays.asList("foo1", "foo2")));
128124
this.model.addAttribute("foo1", "bar1");
129125
this.model.addAttribute("foo2", "bar2");

0 commit comments

Comments
 (0)