From 88c5f5981f29a8815b9521adf49408780a12933b Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 17 Jan 2017 16:19:25 +0100 Subject: [PATCH] Added DataBuffer Encoder/Decoder This commit adds a DataBuffer Encoder and Decoder, and uses it in the annotation-based processing model. Note that these codecs are not used in the functional processing model, since the BodyInserter/BodyExtractor already have methods for writing/reading DataBuffers. Issue: SPR-15148 --- .../core/codec/DataBufferDecoder.java | 57 ++++++++++++++++ .../core/codec/DataBufferEncoder.java | 57 ++++++++++++++++ .../core/codec/DataBufferDecoderTests.java | 63 ++++++++++++++++++ .../core/codec/DataBufferEncoderTests.java | 65 +++++++++++++++++++ .../WebReactiveConfigurationSupport.java | 6 +- .../RequestMappingHandlerAdapter.java | 4 +- ...legatingWebReactiveConfigurationTests.java | 13 ++-- .../WebReactiveConfigurationSupportTests.java | 8 +-- 8 files changed, 263 insertions(+), 10 deletions(-) create mode 100644 spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java create mode 100644 spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java create mode 100644 spring-core/src/test/java/org/springframework/core/codec/DataBufferDecoderTests.java create mode 100644 spring-core/src/test/java/org/springframework/core/codec/DataBufferEncoderTests.java diff --git a/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java new file mode 100644 index 0000000000..dbb1bed32c --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java @@ -0,0 +1,57 @@ +/* + * 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.core.codec; + +import java.util.Map; + +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.util.MimeType; +import org.springframework.util.MimeTypeUtils; + +/** + * Simple pass-through decoder for {@link DataBuffer}s. + *

Note that the "decoded" buffers returned by instances of this class should + * be released after usage by calling + * {@link org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer)}. + * + * @author Arjen Poutsma + * @since 5.0 + */ +public class DataBufferDecoder extends AbstractDecoder { + + public DataBufferDecoder() { + super(MimeTypeUtils.ALL); + } + + + @Override + public boolean canDecode(ResolvableType elementType, MimeType mimeType) { + Class clazz = elementType.getRawClass(); + return (super.canDecode(elementType, mimeType) && DataBuffer.class.isAssignableFrom(clazz)); + } + + @Override + public Flux decode(Publisher inputStream, ResolvableType elementType, + MimeType mimeType, Map hints) { + return Flux.from(inputStream); + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java new file mode 100644 index 0000000000..f9021b9860 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java @@ -0,0 +1,57 @@ +/* + * 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.core.codec; + +import java.util.Map; + +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.util.MimeType; +import org.springframework.util.MimeTypeUtils; + +/** + * Simple pass-through encoder for {@link DataBuffer}s. + * + * @author Arjen Poutsma + * @since 5.0 + */ +public class DataBufferEncoder extends AbstractEncoder { + + public DataBufferEncoder() { + super(MimeTypeUtils.ALL); + } + + + @Override + public boolean canEncode(ResolvableType elementType, MimeType mimeType) { + Class clazz = elementType.getRawClass(); + return (super.canEncode(elementType, mimeType) && DataBuffer.class.isAssignableFrom(clazz)); + } + + @Override + public Flux encode(Publisher inputStream, + DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, + Map hints) { + + return Flux.from(inputStream); + } + +} diff --git a/spring-core/src/test/java/org/springframework/core/codec/DataBufferDecoderTests.java b/spring-core/src/test/java/org/springframework/core/codec/DataBufferDecoderTests.java new file mode 100644 index 0000000000..25a4fd8890 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/codec/DataBufferDecoderTests.java @@ -0,0 +1,63 @@ +/* + * 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.core.codec; + +import java.nio.ByteBuffer; +import java.util.Collections; + +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.util.MimeTypeUtils; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Sebastien Deleuze + */ +public class DataBufferDecoderTests extends AbstractDataBufferAllocatingTestCase { + + private final DataBufferDecoder decoder = new DataBufferDecoder(); + + @Test + public void canDecode() { + assertTrue(this.decoder.canDecode(ResolvableType.forClass(DataBuffer.class), + MimeTypeUtils.TEXT_PLAIN)); + assertFalse(this.decoder.canDecode(ResolvableType.forClass(Integer.class), + MimeTypeUtils.TEXT_PLAIN)); + assertTrue(this.decoder.canDecode(ResolvableType.forClass(DataBuffer.class), + MimeTypeUtils.APPLICATION_JSON)); + } + + @Test + public void decode() { + DataBuffer fooBuffer = stringBuffer("foo"); + DataBuffer barBuffer = stringBuffer("bar"); + Flux source = Flux.just(fooBuffer, barBuffer); + Flux output = this.decoder.decode(source, + ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class), + null, Collections.emptyMap()); + + assertSame(source, output); + } +} diff --git a/spring-core/src/test/java/org/springframework/core/codec/DataBufferEncoderTests.java b/spring-core/src/test/java/org/springframework/core/codec/DataBufferEncoderTests.java new file mode 100644 index 0000000000..28495b97dc --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/codec/DataBufferEncoderTests.java @@ -0,0 +1,65 @@ +/* + * 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.core.codec; + +import java.nio.ByteBuffer; +import java.util.Collections; + +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.core.ResolvableType; +import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.util.MimeTypeUtils; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Sebastien Deleuze + */ +public class DataBufferEncoderTests extends AbstractDataBufferAllocatingTestCase { + + private final DataBufferEncoder encoder = new DataBufferEncoder(); + + @Test + public void canEncode() { + assertTrue(this.encoder.canEncode(ResolvableType.forClass(DataBuffer.class), + MimeTypeUtils.TEXT_PLAIN)); + assertFalse(this.encoder.canEncode(ResolvableType.forClass(Integer.class), + MimeTypeUtils.TEXT_PLAIN)); + assertTrue(this.encoder.canEncode(ResolvableType.forClass(DataBuffer.class), + MimeTypeUtils.APPLICATION_JSON)); + } + + @Test + public void encode() { + DataBuffer fooBuffer = stringBuffer("foo"); + DataBuffer barBuffer = stringBuffer("bar"); + Flux source = Flux.just(fooBuffer, barBuffer); + + Flux output = this.encoder.encode(source, this.bufferFactory, + ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class), + null, Collections.emptyMap()); + + assertSame(source, output); + } + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupport.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupport.java index a09fc4fd0b..536d457b10 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupport.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -34,6 +34,8 @@ 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.Encoder; import org.springframework.core.codec.ResourceDecoder; import org.springframework.core.codec.StringDecoder; @@ -328,6 +330,7 @@ public class WebReactiveConfigurationSupport implements ApplicationContextAware protected final void addDefaultHttpMessageReaders(List> readers) { readers.add(new DecoderHttpMessageReader<>(new ByteArrayDecoder())); readers.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder())); + readers.add(new DecoderHttpMessageReader<>(new DataBufferDecoder())); readers.add(new DecoderHttpMessageReader<>(new StringDecoder())); readers.add(new DecoderHttpMessageReader<>(new ResourceDecoder())); if (jaxb2Present) { @@ -476,6 +479,7 @@ public class WebReactiveConfigurationSupport implements ApplicationContextAware List> sseDataEncoders = new ArrayList<>(); writers.add(new EncoderHttpMessageWriter<>(new ByteArrayEncoder())); writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder())); + writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder())); writers.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder())); writers.add(new ResourceHttpMessageWriter()); if (jaxb2Present) { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 3896741ce4..495534807a 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -37,6 +37,7 @@ import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.codec.ByteArrayDecoder; import org.springframework.core.codec.ByteBufferDecoder; +import org.springframework.core.codec.DataBufferDecoder; import org.springframework.core.codec.StringDecoder; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.HttpMessageReader; @@ -96,6 +97,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory public RequestMappingHandlerAdapter() { this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteArrayDecoder())); this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder())); + this.messageReaders.add(new DecoderHttpMessageReader<>(new DataBufferDecoder())); this.messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder())); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/DelegatingWebReactiveConfigurationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/DelegatingWebReactiveConfigurationTests.java index cd01ba52c6..d37ffb47d4 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/DelegatingWebReactiveConfigurationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/DelegatingWebReactiveConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -37,8 +37,13 @@ import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.any; +import static org.mockito.BDDMockito.doAnswer; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.verify; /** * Test fixture for {@link DelegatingWebReactiveConfiguration} tests. @@ -99,7 +104,7 @@ public class DelegatingWebReactiveConfigurationTests { verify(webReactiveConfigurer).addArgumentResolvers(any()); assertSame(formatterRegistry.getValue(), initializerConversionService); - assertEquals(6, readers.getValue().size()); + assertEquals(7, readers.getValue().size()); } @Test diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupportTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupportTests.java index 271526a635..eea16756d0 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupportTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -139,7 +139,7 @@ public class WebReactiveConfigurationSupportTests { assertNotNull(adapter); List> readers = adapter.getMessageReaders(); - assertEquals(6, readers.size()); + assertEquals(7, readers.size()); assertHasMessageReader(readers, byte[].class, APPLICATION_OCTET_STREAM); assertHasMessageReader(readers, ByteBuffer.class, APPLICATION_OCTET_STREAM); @@ -189,7 +189,7 @@ public class WebReactiveConfigurationSupportTests { assertEquals(0, handler.getOrder()); List> writers = handler.getMessageWriters(); - assertEquals(7, writers.size()); + assertEquals(8, writers.size()); assertHasMessageWriter(writers, byte[].class, APPLICATION_OCTET_STREAM); assertHasMessageWriter(writers, ByteBuffer.class, APPLICATION_OCTET_STREAM); @@ -215,7 +215,7 @@ public class WebReactiveConfigurationSupportTests { assertEquals(100, handler.getOrder()); List> writers = handler.getMessageWriters(); - assertEquals(7, writers.size()); + assertEquals(8, writers.size()); assertHasMessageWriter(writers, byte[].class, APPLICATION_OCTET_STREAM); assertHasMessageWriter(writers, ByteBuffer.class, APPLICATION_OCTET_STREAM);