diff --git a/spring-core/src/main/java/org/springframework/core/codec/AbstractDataBufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/AbstractDataBufferDecoder.java index b03d8079db..f6a29a747a 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/AbstractDataBufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/AbstractDataBufferDecoder.java @@ -45,6 +45,7 @@ import org.springframework.util.MimeType; * @since 5.0 * @param the element type */ +@SuppressWarnings("deprecation") public abstract class AbstractDataBufferDecoder extends AbstractDecoder { @@ -70,8 +71,14 @@ public abstract class AbstractDataBufferDecoder extends AbstractDecoder { /** * How to decode a {@code DataBuffer} to the target element type. + * @deprecated as of 5.2, please implement + * {@link #decode(DataBuffer, ResolvableType, MimeType, Map)} instead */ - protected abstract T decodeDataBuffer(DataBuffer buffer, ResolvableType elementType, - @Nullable MimeType mimeType, @Nullable Map hints); + @Deprecated + protected T decodeDataBuffer(DataBuffer buffer, ResolvableType elementType, + @Nullable MimeType mimeType, @Nullable Map hints) { + + return decode(buffer, elementType, mimeType, hints); + } } diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java index 24ac760817..65f8222d17 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -45,7 +45,7 @@ public class ByteArrayDecoder extends AbstractDataBufferDecoder { } @Override - protected byte[] decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType, + public byte[] decode(DataBuffer dataBuffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { byte[] result = new byte[dataBuffer.readableByteCount()]; diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java index c60501d2ff..9c1133fb1a 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -48,7 +48,7 @@ public class ByteBufferDecoder extends AbstractDataBufferDecoder { } @Override - protected ByteBuffer decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType, + public ByteBuffer decode(DataBuffer dataBuffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { int byteCount = dataBuffer.readableByteCount(); 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 index 17d6a424ab..34b150378e 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -64,7 +64,7 @@ public class DataBufferDecoder extends AbstractDataBufferDecoder { } @Override - protected DataBuffer decodeDataBuffer(DataBuffer buffer, ResolvableType elementType, + public DataBuffer decode(DataBuffer buffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { if (logger.isDebugEnabled()) { diff --git a/spring-core/src/main/java/org/springframework/core/codec/Decoder.java b/spring-core/src/main/java/org/springframework/core/codec/Decoder.java index 3b37ecfef5..c4e84383f6 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/Decoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/Decoder.java @@ -22,10 +22,12 @@ import java.util.Map; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoProcessor; import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.MimeType; /** @@ -75,6 +77,33 @@ public interface Decoder { Mono decodeToMono(Publisher inputStream, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints); + /** + * Decode a data buffer to an Object of type T. This is useful when the input + * stream consists of discrete messages (or events) and the content for each + * can be decoded on its own. + * @param buffer the {@code DataBuffer} to decode + * @param targetType the expected output type + * @param mimeType the MIME type associated with the data + * @param hints additional information about how to do encode + * @return the decoded value, possibly {@code null} + * @since 5.2 + */ + @SuppressWarnings("ConstantConditions") + default T decode(DataBuffer buffer, ResolvableType targetType, + @Nullable MimeType mimeType, @Nullable Map hints) throws DecodingException { + + MonoProcessor processor = MonoProcessor.create(); + decodeToMono(Mono.just(buffer), targetType, mimeType, hints).subscribeWith(processor); + + Assert.state(processor.isTerminated(), "DataBuffer decoding should have completed."); + Throwable ex = processor.getError(); + if (ex != null) { + throw (ex instanceof CodecException ? (CodecException) ex : + new DecodingException("Failed to decode: " + ex.getMessage(), ex)); + } + return processor.peek(); + } + /** * Return the list of MIME types this decoder supports. */ diff --git a/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java index d2b94d13ea..eeb307cfcd 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java @@ -64,7 +64,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder { } @Override - protected Resource decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType, + public Resource decode(DataBuffer dataBuffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { byte[] bytes = new byte[dataBuffer.readableByteCount()]; 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 28cf7df55e..9001de29c7 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 @@ -202,7 +202,7 @@ public final class StringDecoder extends AbstractDataBufferDecoder { } @Override - protected String decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType, + public String decode(DataBuffer dataBuffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { Charset charset = getCharset(mimeType); diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java index f5f2690256..389d56470f 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java @@ -110,20 +110,26 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple public Mono decodeToMono(Publisher input, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { - return DataBufferUtils.join(input).map(dataBuffer -> { - try { - ObjectReader objectReader = getObjectReader(elementType, hints); - Object value = objectReader.readValue(dataBuffer.asInputStream()); - logValue(value, hints); - return value; - } - catch (IOException ex) { - throw processException(ex); - } - finally { - DataBufferUtils.release(dataBuffer); - } - }); + return DataBufferUtils.join(input) + .map(dataBuffer -> decode(dataBuffer, elementType, mimeType, hints)); + } + + @Override + public Object decode(DataBuffer dataBuffer, ResolvableType targetType, + @Nullable MimeType mimeType, @Nullable Map hints) throws DecodingException { + + try { + ObjectReader objectReader = getObjectReader(targetType, hints); + Object value = objectReader.readValue(dataBuffer.asInputStream()); + logValue(value, hints); + return value; + } + catch (IOException ex) { + throw processException(ex); + } + finally { + DataBufferUtils.release(dataBuffer); + } } private ObjectReader getObjectReader(ResolvableType elementType, @Nullable Map hints) { diff --git a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufDecoder.java index c1f0e17bf7..37d7ae4d90 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufDecoder.java @@ -127,26 +127,32 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder decodeToMono(Publisher inputStream, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { - return DataBufferUtils.join(inputStream).map(dataBuffer -> { - try { - Message.Builder builder = getMessageBuilder(elementType.toClass()); - ByteBuffer buffer = dataBuffer.asByteBuffer(); - builder.mergeFrom(CodedInputStream.newInstance(buffer), this.extensionRegistry); - return builder.build(); - } - catch (IOException ex) { - throw new DecodingException("I/O error while parsing input stream", ex); - } - catch (Exception ex) { - throw new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex); - } - finally { - DataBufferUtils.release(dataBuffer); - } - } - ); + return DataBufferUtils.join(inputStream) + .map(dataBuffer -> decode(dataBuffer, elementType, mimeType, hints)); } + @Override + public Message decode(DataBuffer dataBuffer, ResolvableType targetType, + @Nullable MimeType mimeType, @Nullable Map hints) throws DecodingException { + + try { + Message.Builder builder = getMessageBuilder(targetType.toClass()); + ByteBuffer buffer = dataBuffer.asByteBuffer(); + builder.mergeFrom(CodedInputStream.newInstance(buffer), this.extensionRegistry); + return builder.build(); + } + catch (IOException ex) { + throw new DecodingException("I/O error while parsing input stream", ex); + } + catch (Exception ex) { + throw new DecodingException("Could not read Protobuf message: " + ex.getMessage(), ex); + } + finally { + DataBufferUtils.release(dataBuffer); + } + } + + /** * Create a new {@code Message.Builder} instance for the given class. *

This method uses a ConcurrentHashMap for caching method lookups. diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java index 04227f73e2..dd5517259c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java @@ -143,20 +143,27 @@ public class Jaxb2XmlDecoder extends AbstractDecoder { public Mono decodeToMono(Publisher input, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { - return DataBufferUtils.join(input).map(dataBuffer -> { - try { - Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream()); - List events = new ArrayList<>(); - eventReader.forEachRemaining(event -> events.add((XMLEvent) event)); - return unmarshal(events, elementType.toClass()); - } - catch (XMLStreamException ex) { - throw Exceptions.propagate(ex); - } - finally { - DataBufferUtils.release(dataBuffer); - } - }); + return DataBufferUtils.join(input) + .map(dataBuffer -> decode(dataBuffer, elementType, mimeType, hints)); + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked", "cast"}) // XMLEventReader is Iterator on JDK 9 + public Object decode(DataBuffer dataBuffer, ResolvableType targetType, + @Nullable MimeType mimeType, @Nullable Map hints) throws DecodingException { + + try { + Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream()); + List events = new ArrayList<>(); + eventReader.forEachRemaining(event -> events.add((XMLEvent) event)); + return unmarshal(events, targetType.toClass()); + } + catch (XMLStreamException ex) { + throw Exceptions.propagate(ex); + } + finally { + DataBufferUtils.release(dataBuffer); + } } private Object unmarshal(List events, Class outputClass) {