Polish Encoder and Decoder

This commit is contained in:
Rossen Stoyanchev 2016-06-08 15:06:27 -04:00
parent a8e5e40d97
commit 4e3c439593
18 changed files with 96 additions and 85 deletions

View File

@ -26,35 +26,42 @@ import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
/** /**
* Decode a stream of bytes to a stream of type {@code T}. * Strategy for decoding a {@link DataBuffer} input stream into an output stream
* of elements of type {@code <T>}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @see Encoder * @author Rossen Stoyanchev
* @param <T> the type of elements in the output stream
*/ */
public interface Decoder<T> { public interface Decoder<T> {
/** /**
* Whether the decoder supports the given Java and mime type. * Whether the decoder supports the given target element type and the MIME
* @param type the stream element type to process. * type of the source stream.
* @param mimeType the mime type to process. *
* @param hints Additional information about how to do decode, optional. * @param elementType the target element type for the output stream
* @return {@code true} if can process; {@code false} otherwise * @param mimeType the mime type associated with the stream to decode
* @param hints additional information about how to do decode, optional
* @return {@code true} if supported, {@code false} otherwise
*/ */
boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints); boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints);
/** /**
* Decode an input {@link DataBuffer} stream to an output stream of {@code T}. * Decode a {@link DataBuffer} input stream into a Flux of {@code T}.
* @param inputStream the input stream to process. *
* @param type the stream element type to process. * @param inputStream the {@code DataBuffer} input stream to decode
* @param mimeType the mime type to process. * @param elementType the expected type of elements in the output stream;
* @param hints Additional information about how to do decode, optional. * this type must have been previously passed to the {@link #canDecode}
* @return the output stream * method and it must have returned {@code true}.
* @param mimeType the MIME type associated with the input stream, optional
* @param hints additional information about how to do decode, optional
* @return the output stream with decoded elements
*/ */
Flux<T> decode(Publisher<DataBuffer> inputStream, ResolvableType type, Flux<T> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints); MimeType mimeType, Object... hints);
/** /**
* Return the list of mime types this decoder supports. * Return the list of MIME types this decoder supports.
*/ */
List<MimeType> getSupportedMimeTypes(); List<MimeType> getSupportedMimeTypes();

View File

@ -27,34 +27,41 @@ import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
/** /**
* Encode a stream of Objects of type {@code T} into a stream of bytes. * Strategy to encode a stream of Objects of type {@code <T>} into an output
* stream of bytes.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @see Decoder * @author Rossen Stoyanchev
* @param <T> the type of elements in the input stream
*/ */
public interface Encoder<T> { public interface Encoder<T> {
/** /**
* Indicate whether the given type and mime type can be processed by this encoder. * Whether the encoder supports the given source element type and the MIME
* @param type the stream element type to process. * type for the output stream.
* @param mimeType the mime type to process. *
* @param hints Additional information about how to do decode, optional. * @param elementType the type of elements in the source stream
* @return {@code true} if can process; {@code false} otherwise * @param mimeType the MIME type for the output stream
* @param hints additional information about how to do encode, optional
* @return {@code true} if supported, {@code false} otherwise
*/ */
boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints); boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints);
/** /**
* Encode an input stream of {@code T} to an output {@link DataBuffer} stream. * Encode a stream of Objects of type {@code T} into a {@link DataBuffer}
* @param inputStream the input stream to process. * output stream.
* @param dataBufferFactory a buffer factory used to create the output *
* @param type the stream element type to process. * @param inputStream the input stream of Objects to encode
* @param mimeType the mime type to process. * @param bufferFactory for creating output stream {@code DataBuffer}'s
* @param hints Additional information about how to do decode, optional. * @param elementType the expected type of elements in the input stream;
* this type must have been previously passed to the {@link #canEncode}
* method and it must have returned {@code true}.
* @param mimeType the MIME type for the output stream
* @param hints additional information about how to do encode, optional
* @return the output stream * @return the output stream
*/ */
Flux<DataBuffer> encode(Publisher<? extends T> inputStream, Flux<DataBuffer> encode(Publisher<? extends T> inputStream, DataBufferFactory bufferFactory,
DataBufferFactory dataBufferFactory, ResolvableType type, ResolvableType elementType, MimeType mimeType, Object... hints);
MimeType mimeType, Object... hints);
/** /**
* Return the list of mime types this encoder supports. * Return the list of mime types this encoder supports.

View File

@ -42,7 +42,7 @@ public abstract class AbstractDecoder<T> implements Decoder<T> {
} }
@Override @Override
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (mimeType == null) { if (mimeType == null) {
return true; return true;
} }

View File

@ -42,7 +42,7 @@ public abstract class AbstractEncoder<T> implements Encoder<T> {
} }
@Override @Override
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (mimeType == null) { if (mimeType == null) {
return true; return true;
} }

View File

@ -37,13 +37,13 @@ public abstract class AbstractSingleValueEncoder<T> extends AbstractEncoder<T> {
@Override @Override
public final Flux<DataBuffer> encode(Publisher<? extends T> inputStream, public final Flux<DataBuffer> encode(Publisher<? extends T> inputStream,
DataBufferFactory dataBufferFactory, ResolvableType type, MimeType mimeType, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType,
Object... hints) { Object... hints) {
return Flux.from(inputStream). return Flux.from(inputStream).
take(1). take(1).
concatMap(t -> { concatMap(t -> {
try { try {
return encode(t, dataBufferFactory, type, mimeType); return encode(t, bufferFactory, elementType, mimeType);
} }
catch (Exception ex) { catch (Exception ex) {
return Flux.error(ex); return Flux.error(ex);

View File

@ -40,13 +40,13 @@ public class ByteBufferDecoder extends AbstractDecoder<ByteBuffer> {
@Override @Override
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
return (super.canDecode(type, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz)); return (super.canDecode(elementType, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz));
} }
@Override @Override
public Flux<ByteBuffer> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<ByteBuffer> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
return Flux.from(inputStream).map((dataBuffer) -> { return Flux.from(inputStream).map((dataBuffer) -> {
ByteBuffer copy = ByteBuffer.allocate(dataBuffer.readableByteCount()); ByteBuffer copy = ByteBuffer.allocate(dataBuffer.readableByteCount());

View File

@ -38,17 +38,17 @@ public class ByteBufferEncoder extends AbstractEncoder<ByteBuffer> {
@Override @Override
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
return (super.canEncode(type, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz)); return (super.canEncode(elementType, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz));
} }
@Override @Override
public Flux<DataBuffer> encode(Publisher<? extends ByteBuffer> inputStream, public Flux<DataBuffer> encode(Publisher<? extends ByteBuffer> inputStream,
DataBufferFactory dataBufferFactory, ResolvableType type, MimeType mimeType, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType,
Object... hints) { Object... hints) {
return Flux.from(inputStream).map(dataBufferFactory::wrap); return Flux.from(inputStream).map(bufferFactory::wrap);
} }
} }

View File

@ -61,14 +61,14 @@ public class JacksonJsonDecoder extends AbstractDecoder<Object> {
} }
@Override @Override
public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
ObjectReader reader = this.mapper.readerFor(type.getRawClass()); ObjectReader reader = this.mapper.readerFor(elementType.getRawClass());
Flux<DataBuffer> stream = Flux.from(inputStream); Flux<DataBuffer> stream = Flux.from(inputStream);
if (this.preProcessor != null) { if (this.preProcessor != null) {
stream = this.preProcessor.decode(inputStream, type, mimeType, hints); stream = this.preProcessor.decode(inputStream, elementType, mimeType, hints);
} }
return stream.map(dataBuffer -> { return stream.map(dataBuffer -> {

View File

@ -64,24 +64,24 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
@Override @Override
public Flux<DataBuffer> encode(Publisher<?> inputStream, public Flux<DataBuffer> encode(Publisher<?> inputStream,
DataBufferFactory dataBufferFactory, ResolvableType type, MimeType mimeType, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType,
Object... hints) { Object... hints) {
if (inputStream instanceof Mono) { if (inputStream instanceof Mono) {
// single object // single object
return Flux.from(inputStream) return Flux.from(inputStream)
.map(value -> serialize(value, dataBufferFactory)); .map(value -> serialize(value, bufferFactory));
} }
else { else {
// array // array
Mono<DataBuffer> startArray = Mono<DataBuffer> startArray =
Mono.just(dataBufferFactory.wrap(START_ARRAY_BUFFER)); Mono.just(bufferFactory.wrap(START_ARRAY_BUFFER));
Flux<DataBuffer> arraySeparators = Flux<DataBuffer> arraySeparators =
Mono.just(dataBufferFactory.wrap(SEPARATOR_BUFFER)).repeat(); Mono.just(bufferFactory.wrap(SEPARATOR_BUFFER)).repeat();
Mono<DataBuffer> endArray = Mono<DataBuffer> endArray =
Mono.just(dataBufferFactory.wrap(END_ARRAY_BUFFER)); Mono.just(bufferFactory.wrap(END_ARRAY_BUFFER));
Flux<DataBuffer> serializedObjects = Flux.from(inputStream) Flux<DataBuffer> serializedObjects = Flux.from(inputStream)
.map(value -> serialize(value, dataBufferFactory)); .map(value -> serialize(value, bufferFactory));
Flux<DataBuffer> array = Flux.zip(serializedObjects, arraySeparators) Flux<DataBuffer> array = Flux.zip(serializedObjects, arraySeparators)
.flatMap(tuple -> Flux.just(tuple.getT1(), tuple.getT2())); .flatMap(tuple -> Flux.just(tuple.getT1(), tuple.getT2()));

View File

@ -69,9 +69,9 @@ public class Jaxb2Decoder extends AbstractDecoder<Object> {
} }
@Override @Override
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (super.canDecode(type, mimeType, hints)) { if (super.canDecode(elementType, mimeType, hints)) {
Class<?> outputClass = type.getRawClass(); Class<?> outputClass = elementType.getRawClass();
return outputClass.isAnnotationPresent(XmlRootElement.class) || return outputClass.isAnnotationPresent(XmlRootElement.class) ||
outputClass.isAnnotationPresent(XmlType.class); outputClass.isAnnotationPresent(XmlType.class);
} }
@ -81,9 +81,9 @@ public class Jaxb2Decoder extends AbstractDecoder<Object> {
} }
@Override @Override
public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
Class<?> outputClass = type.getRawClass(); Class<?> outputClass = elementType.getRawClass();
Flux<XMLEvent> xmlEventFlux = Flux<XMLEvent> xmlEventFlux =
this.xmlEventDecoder.decode(inputStream, null, mimeType); this.xmlEventDecoder.decode(inputStream, null, mimeType);

View File

@ -48,9 +48,9 @@ public class Jaxb2Encoder extends AbstractSingleValueEncoder<Object> {
} }
@Override @Override
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (super.canEncode(type, mimeType, hints)) { if (super.canEncode(elementType, mimeType, hints)) {
Class<?> outputClass = type.getRawClass(); Class<?> outputClass = elementType.getRawClass();
return outputClass.isAnnotationPresent(XmlRootElement.class) || return outputClass.isAnnotationPresent(XmlRootElement.class) ||
outputClass.isAnnotationPresent(XmlType.class); outputClass.isAnnotationPresent(XmlType.class);
} }

View File

@ -93,7 +93,7 @@ public class JsonObjectDecoder extends AbstractDecoder<DataBuffer> {
} }
@Override @Override
public Flux<DataBuffer> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<DataBuffer> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
return Flux.from(inputStream) return Flux.from(inputStream)

View File

@ -43,17 +43,17 @@ public class ResourceDecoder extends AbstractDecoder<Resource> {
} }
@Override @Override
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
return (InputStreamResource.class.equals(clazz) || return (InputStreamResource.class.equals(clazz) ||
clazz.isAssignableFrom(ByteArrayResource.class)) && clazz.isAssignableFrom(ByteArrayResource.class)) &&
super.canDecode(type, mimeType, hints); super.canDecode(elementType, mimeType, hints);
} }
@Override @Override
public Flux<Resource> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<Resource> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
Mono<byte[]> byteArray = Flux.from(inputStream). Mono<byte[]> byteArray = Flux.from(inputStream).
reduce(DataBuffer::write). reduce(DataBuffer::write).

View File

@ -52,9 +52,9 @@ public class ResourceEncoder extends AbstractSingleValueEncoder<Resource> {
} }
@Override @Override
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
return (super.canEncode(type, mimeType, hints) && return (super.canEncode(elementType, mimeType, hints) &&
Resource.class.isAssignableFrom(clazz)); Resource.class.isAssignableFrom(clazz));
} }

View File

@ -70,13 +70,13 @@ public class StringDecoder extends AbstractDecoder<String> {
} }
@Override @Override
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
return super.canDecode(type, mimeType, hints) && return super.canDecode(elementType, mimeType, hints) &&
String.class.equals(type.getRawClass()); String.class.equals(elementType.getRawClass());
} }
@Override @Override
public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
Flux<DataBuffer> inputFlux = Flux.from(inputStream); Flux<DataBuffer> inputFlux = Flux.from(inputStream);
if (this.splitOnNewline) { if (this.splitOnNewline) {

View File

@ -43,14 +43,14 @@ public class StringEncoder extends AbstractEncoder<String> {
@Override @Override
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) { public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
Class<?> clazz = type.getRawClass(); Class<?> clazz = elementType.getRawClass();
return (super.canEncode(type, mimeType, hints) && String.class.equals(clazz)); return (super.canEncode(elementType, mimeType, hints) && String.class.equals(clazz));
} }
@Override @Override
public Flux<DataBuffer> encode(Publisher<? extends String> inputStream, public Flux<DataBuffer> encode(Publisher<? extends String> inputStream,
DataBufferFactory dataBufferFactory, ResolvableType type, MimeType mimeType, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType,
Object... hints) { Object... hints) {
Charset charset; Charset charset;
if (mimeType != null && mimeType.getCharSet() != null) { if (mimeType != null && mimeType.getCharSet() != null) {
@ -61,7 +61,7 @@ public class StringEncoder extends AbstractEncoder<String> {
} }
return Flux.from(inputStream).map(s -> { return Flux.from(inputStream).map(s -> {
byte[] bytes = s.getBytes(charset); byte[] bytes = s.getBytes(charset);
DataBuffer dataBuffer = dataBufferFactory.allocateBuffer(bytes.length); DataBuffer dataBuffer = bufferFactory.allocateBuffer(bytes.length);
dataBuffer.write(bytes); dataBuffer.write(bytes);
return dataBuffer; return dataBuffer;
}); });

View File

@ -83,7 +83,7 @@ public class XmlEventDecoder extends AbstractDecoder<XMLEvent> {
} }
@Override @Override
public Flux<XMLEvent> decode(Publisher<DataBuffer> inputStream, ResolvableType type, public Flux<XMLEvent> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) { MimeType mimeType, Object... hints) {
Flux<DataBuffer> flux = Flux.from(inputStream); Flux<DataBuffer> flux = Flux.from(inputStream);
if (useAalto && aaltoPresent) { if (useAalto && aaltoPresent) {

View File

@ -19,8 +19,6 @@ package org.springframework.web.reactive.result.method.annotation;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.xml.crypto.Data;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -28,7 +26,6 @@ import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.reactive.HttpMessageConverter; import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;