Add option to decode from a DataBuffer
See gh-22782
This commit is contained in:
@@ -45,6 +45,7 @@ import org.springframework.util.MimeType;
|
||||
* @since 5.0
|
||||
* @param <T> the element type
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
|
||||
|
||||
|
||||
@@ -70,8 +71,14 @@ public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
|
||||
|
||||
/**
|
||||
* 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<String, Object> hints);
|
||||
@Deprecated
|
||||
protected T decodeDataBuffer(DataBuffer buffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
return decode(buffer, elementType, mimeType, hints);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<byte[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
public byte[] decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
byte[] result = new byte[dataBuffer.readableByteCount()];
|
||||
|
||||
@@ -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<ByteBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
public ByteBuffer decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
int byteCount = dataBuffer.readableByteCount();
|
||||
|
||||
@@ -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<DataBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataBuffer decodeDataBuffer(DataBuffer buffer, ResolvableType elementType,
|
||||
public DataBuffer decode(DataBuffer buffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
@@ -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<T> {
|
||||
Mono<T> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> 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<String, Object> hints) throws DecodingException {
|
||||
|
||||
MonoProcessor<T> 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.
|
||||
*/
|
||||
|
||||
@@ -64,7 +64,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
public Resource decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||
|
||||
@@ -202,7 +202,7 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
public String decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Charset charset = getCharset(mimeType);
|
||||
|
||||
@@ -110,20 +110,26 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||
public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> 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<String, Object> 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<String, Object> hints) {
|
||||
|
||||
@@ -127,26 +127,32 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
|
||||
public Mono<Message> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> 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<String, Object> 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.
|
||||
* <p>This method uses a ConcurrentHashMap for caching method lookups.
|
||||
|
||||
@@ -143,20 +143,27 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
|
||||
public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
return DataBufferUtils.join(input).map(dataBuffer -> {
|
||||
try {
|
||||
Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream());
|
||||
List<XMLEvent> 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<Object> on JDK 9
|
||||
public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
|
||||
|
||||
try {
|
||||
Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream());
|
||||
List<XMLEvent> 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<XMLEvent> events, Class<?> outputClass) {
|
||||
|
||||
Reference in New Issue
Block a user