Polish
This commit is contained in:
@@ -30,10 +30,17 @@ public abstract class AbstractDecoder<T> implements Decoder<T> {
|
||||
|
||||
private List<MimeType> supportedMimeTypes = Collections.emptyList();
|
||||
|
||||
|
||||
public AbstractDecoder(MimeType... supportedMimeTypes) {
|
||||
this.supportedMimeTypes = Arrays.asList(supportedMimeTypes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MimeType> getSupportedMimeTypes() {
|
||||
return this.supportedMimeTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) {
|
||||
for (MimeType supportedMimeType : this.supportedMimeTypes) {
|
||||
@@ -44,9 +51,4 @@ public abstract class AbstractDecoder<T> implements Decoder<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MimeType> getSupportedMimeTypes() {
|
||||
return this.supportedMimeTypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,14 +29,16 @@ import org.springframework.util.MimeTypeUtils;
|
||||
*/
|
||||
public class ByteBufferDecoder extends AbstractDecoder<ByteBuffer> {
|
||||
|
||||
|
||||
public ByteBufferDecoder() {
|
||||
super(MimeTypeUtils.ALL);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType type, MimeType mimeType, Object... hints) {
|
||||
return super.canDecode(type, mimeType, hints)
|
||||
&& ByteBuffer.class.isAssignableFrom(type.getRawClass());
|
||||
Class<?> clazz = type.getRawClass();
|
||||
return (super.canDecode(type, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,4 +47,5 @@ public class ByteBufferDecoder extends AbstractDecoder<ByteBuffer> {
|
||||
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import org.springframework.reactive.codec.encoder.Encoder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Decode from a bytes stream to a stream of {@code T}.
|
||||
* Decode a stream of bytes to a stream of type {@code T}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @see Encoder
|
||||
@@ -34,7 +34,7 @@ import org.springframework.util.MimeType;
|
||||
public interface Decoder<T> {
|
||||
|
||||
/**
|
||||
* Indicate whether the given type and mime type can be processed by this decoder.
|
||||
* Whether the decoder supports the given Java and mime type.
|
||||
* @param type the stream element type to process.
|
||||
* @param mimeType the mime type to process.
|
||||
* @param hints Additional information about how to do decode, optional.
|
||||
@@ -50,11 +50,11 @@ public interface Decoder<T> {
|
||||
* @param hints Additional information about how to do decode, optional.
|
||||
* @return the output stream
|
||||
*/
|
||||
Publisher<T> decode(Publisher<ByteBuffer> inputStream, ResolvableType type, MimeType mimeType, Object... hints);
|
||||
Publisher<T> decode(Publisher<ByteBuffer> inputStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MimeType} objects supported by this codec.
|
||||
* @return the list of supported mime types
|
||||
* Return the list of mime types this decoder supports.
|
||||
*/
|
||||
List<MimeType> getSupportedMimeTypes();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.reactive.codec.encoder.JacksonJsonEncoder;
|
||||
import org.springframework.reactive.io.ByteBufferInputStream;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
|
||||
/**
|
||||
* Decode from a bytes stream of JSON objects to a stream of {@code Object} (POJO).
|
||||
*
|
||||
@@ -59,16 +60,20 @@ public class JacksonJsonDecoder extends AbstractDecoder<Object> {
|
||||
this.preProcessor = preProcessor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Publisher<Object> decode(Publisher<ByteBuffer> inputStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints) {
|
||||
|
||||
ObjectReader reader = this.mapper.readerFor(type.getRawClass());
|
||||
Publisher<ByteBuffer> decodedStream = this.preProcessor == null ? inputStream :
|
||||
this.preProcessor.decode(inputStream, type, mimeType, hints);
|
||||
return Publishers.map(decodedStream, chunk -> {
|
||||
|
||||
if (this.preProcessor != null) {
|
||||
inputStream = this.preProcessor.decode(inputStream, type, mimeType, hints);
|
||||
}
|
||||
|
||||
return Publishers.map(inputStream, content -> {
|
||||
try {
|
||||
return reader.readValue(new ByteBufferInputStream(chunk));
|
||||
return reader.readValue(new ByteBufferInputStream(content));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new CodecException("Error while reading the data", e);
|
||||
|
||||
@@ -54,10 +54,12 @@ public class Jaxb2Decoder extends AbstractDecoder<Object> {
|
||||
|
||||
private final ConcurrentMap<Class<?>, JAXBContext> jaxbContexts = new ConcurrentHashMap<>(64);
|
||||
|
||||
|
||||
public Jaxb2Decoder() {
|
||||
super(MediaType.APPLICATION_XML, MediaType.TEXT_XML);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Publisher<Object> decode(Publisher<ByteBuffer> inputStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints) {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class StringDecoder extends AbstractDecoder<String> {
|
||||
else {
|
||||
charset = DEFAULT_CHARSET;
|
||||
}
|
||||
return Publishers.map(inputStream, chunk -> new String(new Buffer(chunk).asBytes(), charset));
|
||||
return Publishers.map(inputStream, content -> new String(new Buffer(content).asBytes(), charset));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,10 +30,17 @@ public abstract class AbstractEncoder<T> implements Encoder<T> {
|
||||
|
||||
private List<MimeType> supportedMimeTypes = Collections.emptyList();
|
||||
|
||||
|
||||
public AbstractEncoder(MimeType... supportedMimeTypes) {
|
||||
this.supportedMimeTypes = Arrays.asList(supportedMimeTypes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MimeType> getSupportedMimeTypes() {
|
||||
return this.supportedMimeTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) {
|
||||
for (MimeType supportedMimeType : this.supportedMimeTypes) {
|
||||
@@ -44,9 +51,4 @@ public abstract class AbstractEncoder<T> implements Encoder<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MimeType> getSupportedMimeTypes() {
|
||||
return this.supportedMimeTypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,20 +29,24 @@ import org.springframework.util.MimeTypeUtils;
|
||||
*/
|
||||
public class ByteBufferEncoder extends AbstractEncoder<ByteBuffer> {
|
||||
|
||||
|
||||
public ByteBufferEncoder() {
|
||||
super(MimeTypeUtils.ALL);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) {
|
||||
return super.canEncode(type, mimeType, hints)
|
||||
&& ByteBuffer.class.isAssignableFrom(type.getRawClass());
|
||||
Class<?> clazz = type.getRawClass();
|
||||
return (super.canEncode(type, mimeType, hints) && ByteBuffer.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<ByteBuffer> encode(Publisher<? extends ByteBuffer> inputStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints) {
|
||||
|
||||
return (Publisher<ByteBuffer>)inputStream;
|
||||
//noinspection unchecked
|
||||
return (Publisher<ByteBuffer>) inputStream;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import org.springframework.reactive.codec.decoder.Decoder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Encode from a stream of {@code T} to a bytes stream.
|
||||
* Encode a stream of Objects of type {@code T} into a stream of bytes.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @see Decoder
|
||||
@@ -50,11 +50,11 @@ public interface Encoder<T> {
|
||||
* @param hints Additional information about how to do decode, optional.
|
||||
* @return the output stream
|
||||
*/
|
||||
Publisher<ByteBuffer> encode(Publisher<? extends T> inputStream, ResolvableType type, MimeType mimeType, Object... hints);
|
||||
Publisher<ByteBuffer> encode(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MimeType} objects supported by this codec.
|
||||
* @return the list of supported mime types
|
||||
* Return the list of mime types this encoder supports.
|
||||
*/
|
||||
List<MimeType> getSupportedMimeTypes();
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
|
||||
|
||||
private Encoder<ByteBuffer> postProcessor;
|
||||
|
||||
|
||||
public JacksonJsonEncoder() {
|
||||
this(new ObjectMapper(), null);
|
||||
}
|
||||
@@ -51,6 +52,7 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
|
||||
this(new ObjectMapper(), postProcessor);
|
||||
}
|
||||
|
||||
|
||||
public JacksonJsonEncoder(ObjectMapper mapper, Encoder<ByteBuffer> postProcessor) {
|
||||
super(new MimeType("application", "json", StandardCharsets.UTF_8),
|
||||
new MimeType("application", "*+json", StandardCharsets.UTF_8));
|
||||
@@ -67,13 +69,17 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
|
||||
BufferOutputStream outputStream = new BufferOutputStream(buffer);
|
||||
try {
|
||||
this.mapper.writeValue(outputStream, value);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new CodecException("Error while writing the data", e);
|
||||
}
|
||||
buffer.flip();
|
||||
return buffer.byteBuffer();
|
||||
});
|
||||
return this.postProcessor == null ? stream : this.postProcessor.encode(stream, type, mimeType, hints);
|
||||
if (this.postProcessor != null) {
|
||||
stream = this.postProcessor.encode(stream, type, mimeType, hints);
|
||||
};
|
||||
return stream;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ public class Jaxb2Encoder extends AbstractEncoder<Object> {
|
||||
super(MediaType.APPLICATION_XML, MediaType.TEXT_XML);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Publisher<ByteBuffer> encode(Publisher<? extends Object> messageStream, ResolvableType type,
|
||||
MimeType mimeType, Object... hints) {
|
||||
|
||||
@@ -42,10 +42,11 @@ public class StringEncoder extends AbstractEncoder<String> {
|
||||
super(new MimeType("text", "plain", DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType type, MimeType mimeType, Object... hints) {
|
||||
return super.canEncode(type, mimeType, hints)
|
||||
&& String.class.isAssignableFrom(type.getRawClass());
|
||||
Class<?> clazz = type.getRawClass();
|
||||
return (super.canEncode(type, mimeType, hints) && String.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2015 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.reactive.codec.support;
|
||||
|
||||
import org.springframework.reactive.codec.decoder.Decoder;
|
||||
import org.springframework.reactive.codec.encoder.Encoder;
|
||||
|
||||
/**
|
||||
* Utility methods for dealing with codec hints.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @see Encoder
|
||||
* @see Decoder
|
||||
*/
|
||||
public abstract class HintUtils {
|
||||
|
||||
public static <T> T getHintByClass(Class<T> clazz, Object[] hints) {
|
||||
return getHintByClass(clazz, hints, null);
|
||||
}
|
||||
|
||||
public static <T> T getHintByClass(Class<T> clazz, Object[] hints, T defaultValue) {
|
||||
for (Object hint : hints) {
|
||||
if (hint.getClass().isAssignableFrom(clazz)) {
|
||||
return (T) hint;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static boolean containsHint(Object hint, Object[] hints) {
|
||||
for (Object h : hints) {
|
||||
if (h.equals(hint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,15 +39,15 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
*/
|
||||
public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final List<Decoder<?>> deserializers;
|
||||
private final List<Decoder<?>> decoders;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
||||
public RequestBodyArgumentResolver(List<Decoder<?>> deserializers, ConversionService service) {
|
||||
Assert.notEmpty(deserializers, "At least one deserializer is required.");
|
||||
public RequestBodyArgumentResolver(List<Decoder<?>> decoders, ConversionService service) {
|
||||
Assert.notEmpty(decoders, "At least one decoder is required.");
|
||||
Assert.notNull(service, "'conversionService' is required.");
|
||||
this.deserializers = deserializers;
|
||||
this.decoders = decoders;
|
||||
this.conversionService = service;
|
||||
}
|
||||
|
||||
@@ -61,16 +61,19 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
|
||||
public Publisher<Object> resolveArgument(MethodParameter parameter, ReactiveServerHttpRequest request) {
|
||||
MediaType mediaType = resolveMediaType(request);
|
||||
ResolvableType type = ResolvableType.forMethodParameter(parameter);
|
||||
Publisher<ByteBuffer> inputStream = request.getBody();
|
||||
Publisher<?> elementStream = inputStream;
|
||||
Publisher<ByteBuffer> body = request.getBody();
|
||||
Publisher<?> elementStream = body;
|
||||
ResolvableType elementType = type.hasGenerics() ? type.getGeneric(0) : type;
|
||||
Decoder<?> deserializer = resolveDeserializer(elementType, mediaType);
|
||||
if (deserializer != null) {
|
||||
elementStream = deserializer.decode(inputStream, elementType, mediaType);
|
||||
|
||||
Decoder<?> decoder = resolveDecoder(elementType, mediaType);
|
||||
if (decoder != null) {
|
||||
elementStream = decoder.decode(body, elementType, mediaType);
|
||||
}
|
||||
|
||||
if (this.conversionService.canConvert(Publisher.class, type.getRawClass())) {
|
||||
return Publishers.just(this.conversionService.convert(elementStream, type.getRawClass()));
|
||||
}
|
||||
|
||||
return Publishers.map(elementStream, element -> element);
|
||||
}
|
||||
|
||||
@@ -81,10 +84,10 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
|
||||
return ( mediaTypes.size() > 0 ? mediaTypes.get(0) : MediaType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
private Decoder<?> resolveDeserializer(ResolvableType type, MediaType mediaType, Object... hints) {
|
||||
for (Decoder<?> deserializer : this.deserializers) {
|
||||
if (deserializer.canDecode(type, mediaType, hints)) {
|
||||
return deserializer;
|
||||
private Decoder<?> resolveDecoder(ResolvableType type, MediaType mediaType, Object... hints) {
|
||||
for (Decoder<?> decoder : this.decoders) {
|
||||
if (decoder.canDecode(type, mediaType, hints)) {
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -48,17 +48,17 @@ import org.springframework.web.method.HandlerMethod;
|
||||
*/
|
||||
public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered {
|
||||
|
||||
private final List<Encoder<?>> serializers;
|
||||
private final List<Encoder<?>> encoders;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private int order = 0;
|
||||
|
||||
|
||||
public ResponseBodyResultHandler(List<Encoder<?>> serializers, ConversionService service) {
|
||||
Assert.notEmpty(serializers, "At least one serializers is required.");
|
||||
public ResponseBodyResultHandler(List<Encoder<?>> encoders, ConversionService service) {
|
||||
Assert.notEmpty(encoders, "At least one encoders is required.");
|
||||
Assert.notNull(service, "'conversionService' is required.");
|
||||
this.serializers = serializers;
|
||||
this.encoders = encoders;
|
||||
this.conversionService = service;
|
||||
}
|
||||
|
||||
@@ -89,45 +89,46 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
|
||||
ReactiveServerHttpResponse response, HandlerResult result) {
|
||||
|
||||
Object value = result.getValue();
|
||||
HandlerMethod handlerMethod = (HandlerMethod) result.getHandler();
|
||||
MethodParameter returnType = handlerMethod.getReturnValueType(value);
|
||||
|
||||
if (value == null) {
|
||||
return Publishers.empty();
|
||||
}
|
||||
|
||||
ResolvableType type = ResolvableType.forMethodParameter(returnType);
|
||||
MediaType mediaType = resolveMediaType(request);
|
||||
HandlerMethod hm = (HandlerMethod) result.getHandler();
|
||||
ResolvableType returnType = ResolvableType.forMethodParameter(hm.getReturnValueType(value));
|
||||
|
||||
Publisher<?> elementStream;
|
||||
ResolvableType elementType;
|
||||
if (conversionService.canConvert(type.getRawClass(), Publisher.class)) {
|
||||
elementStream = conversionService.convert(value, Publisher.class);
|
||||
elementType = type.getGeneric(0);
|
||||
if (this.conversionService.canConvert(returnType.getRawClass(), Publisher.class)) {
|
||||
elementStream = this.conversionService.convert(value, Publisher.class);
|
||||
elementType = returnType.getGeneric(0);
|
||||
}
|
||||
else {
|
||||
elementStream = Publishers.just(value);
|
||||
elementType = type;
|
||||
elementType = returnType;
|
||||
}
|
||||
|
||||
Encoder<?> serializer = resolveSerializer(elementType, mediaType);
|
||||
|
||||
if (serializer != null) {
|
||||
Publisher<ByteBuffer> outputStream = serializer.encode((Publisher)elementStream, type, mediaType);
|
||||
if (mediaType == null || mediaType.isWildcardType() || mediaType.isWildcardSubtype()) {
|
||||
List<MimeType> mimeTypes = serializer.getSupportedMimeTypes();
|
||||
if (!mimeTypes.isEmpty()) {
|
||||
MimeType mimeType = mimeTypes.get(0);
|
||||
mediaType = new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters());
|
||||
}
|
||||
}
|
||||
if (mediaType != null && !mediaType.equals(MediaType.ALL)) {
|
||||
response.getHeaders().setContentType(mediaType);
|
||||
}
|
||||
return response.setBody(outputStream);
|
||||
MediaType mediaType = resolveMediaType(request);
|
||||
Encoder<?> encoder = resolveEncoder(elementType, mediaType);
|
||||
if (encoder == null) {
|
||||
return Publishers.error(new IllegalStateException(
|
||||
"Return value type '" + returnType +
|
||||
"' with media type '" + mediaType + "' not supported"));
|
||||
}
|
||||
String returnTypeName = returnType.getParameterType().getName();
|
||||
return Publishers.error(new IllegalStateException("Return value type '" + returnTypeName +
|
||||
"' with media type '" + mediaType + "' not supported"));
|
||||
|
||||
Publisher<ByteBuffer> outputStream = encoder.encode((Publisher) elementStream, returnType, mediaType);
|
||||
if (mediaType == null || mediaType.isWildcardType() || mediaType.isWildcardSubtype()) {
|
||||
List<MimeType> mimeTypes = encoder.getSupportedMimeTypes();
|
||||
if (!mimeTypes.isEmpty()) {
|
||||
MimeType mimeType = mimeTypes.get(0);
|
||||
mediaType = new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaType != null && !mediaType.equals(MediaType.ALL)) {
|
||||
response.getHeaders().setContentType(mediaType);
|
||||
}
|
||||
|
||||
return response.setBody(outputStream);
|
||||
}
|
||||
|
||||
private MediaType resolveMediaType(ReactiveServerHttpRequest request) {
|
||||
@@ -137,10 +138,10 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
|
||||
return ( mediaTypes.size() > 0 ? mediaTypes.get(0) : MediaType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
private Encoder<?> resolveSerializer(ResolvableType type, MediaType mediaType, Object... hints) {
|
||||
for (Encoder<?> serializer : this.serializers) {
|
||||
if (serializer.canEncode(type, mediaType, hints)) {
|
||||
return serializer;
|
||||
private Encoder<?> resolveEncoder(ResolvableType type, MediaType mediaType, Object... hints) {
|
||||
for (Encoder<?> encoder : this.encoders) {
|
||||
if (encoder.canEncode(type, mediaType, hints)) {
|
||||
return encoder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user