Minor revision of reactive support layout (ahead of 5.0 M1)

DataSourceUtils moved to main core.io.buffer package.
Consistently named Jackson2JsonDecoder/Encoder and Jaxb2XmlDecoder/Encoder.
Plenty of related polishing.
This commit is contained in:
Juergen Hoeller
2016-07-26 15:39:32 +02:00
parent 3d6a5bcd66
commit c13f8419f9
51 changed files with 335 additions and 378 deletions

View File

@@ -28,12 +28,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
/**
* Abstract base class for Jackson based decoder/encoder implementations.
*
* @author Sebastien Deleuze
*/
public class AbstractJacksonJsonCodec {
public class AbstractJackson2Codec {
protected static final List<MimeType> JSON_MIME_TYPES = Arrays.asList(
new MimeType("application", "json", StandardCharsets.UTF_8),
@@ -42,10 +45,17 @@ public class AbstractJacksonJsonCodec {
protected final ObjectMapper mapper;
protected AbstractJacksonJsonCodec(ObjectMapper mapper) {
/**
* Create a new Jackson codec for the given mapper.
* @param mapper the Jackson ObjectMapper to use
*/
protected AbstractJackson2Codec(ObjectMapper mapper) {
Assert.notNull(mapper, "ObjectMapper must not be null");
this.mapper = mapper;
}
/**
* Return the Jackson {@link JavaType} for the specified type and context class.
* <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)},

View File

@@ -32,35 +32,35 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.codec.CodecException;
import org.springframework.core.codec.Decoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferUtils;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
/**
* Decode a byte stream into JSON and convert to Object's with Jackson.
* Decode a byte stream into JSON and convert to Object's with Jackson 2.6+.
*
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
* @since 5.0
* @see JacksonJsonEncoder
* @see Jackson2JsonEncoder
*/
public class JacksonJsonDecoder extends AbstractJacksonJsonCodec implements Decoder<Object> {
public class Jackson2JsonDecoder extends AbstractJackson2Codec implements Decoder<Object> {
private final JsonObjectDecoder fluxObjectDecoder = new JsonObjectDecoder(true);
private final JsonObjectDecoder monoObjectDecoder = new JsonObjectDecoder(false);
public JacksonJsonDecoder() {
public Jackson2JsonDecoder() {
super(Jackson2ObjectMapperBuilder.json().build());
}
public JacksonJsonDecoder(ObjectMapper mapper) {
public Jackson2JsonDecoder(ObjectMapper mapper) {
super(mapper);
}
@Override
public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (mimeType == null) {
@@ -96,23 +96,23 @@ public class JacksonJsonDecoder extends AbstractJacksonJsonCodec implements Deco
Assert.notNull(inputStream, "'inputStream' must not be null");
Assert.notNull(elementType, "'elementType' must not be null");
MethodParameter methodParameter = (elementType.getSource() instanceof MethodParameter ?
(MethodParameter)elementType.getSource() : null);
Class<?> contextClass = (methodParameter != null ? methodParameter.getContainingClass() : null);
MethodParameter methodParam = (elementType.getSource() instanceof MethodParameter ?
(MethodParameter) elementType.getSource() : null);
Class<?> contextClass = (methodParam != null ? methodParam.getContainingClass() : null);
JavaType javaType = getJavaType(elementType.getType(), contextClass);
ObjectReader reader;
if (methodParameter != null && methodParameter.getParameter().getAnnotation(JsonView.class) != null) {
JsonView annotation = methodParameter.getParameter().getAnnotation(JsonView.class);
Class<?>[] classes = annotation.value();
ObjectReader reader;
JsonView jsonView = (methodParam != null ? methodParam.getParameterAnnotation(JsonView.class) : null);
if (jsonView != null) {
Class<?>[] classes = jsonView.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for response body advice with exactly 1 class argument: " + methodParameter);
throw new IllegalArgumentException("@JsonView only supported for response body advice " +
"with exactly 1 class argument: " + methodParam);
}
reader = mapper.readerWithView(classes[0]).forType(javaType);
reader = this.mapper.readerWithView(classes[0]).forType(javaType);
}
else {
reader = mapper.readerFor(javaType);
reader = this.mapper.readerFor(javaType);
}
return objectDecoder.decode(inputStream, elementType, mimeType, hints)
@@ -122,8 +122,8 @@ public class JacksonJsonDecoder extends AbstractJacksonJsonCodec implements Deco
DataBufferUtils.release(dataBuffer);
return value;
}
catch (IOException e) {
return Flux.error(new CodecException("Error while reading the data", e));
catch (IOException ex) {
return Flux.error(new CodecException("Error while reading the data", ex));
}
});
}

View File

@@ -41,14 +41,15 @@ import org.springframework.util.Assert;
import org.springframework.util.MimeType;
/**
* Encode from an {@code Object} stream to a byte stream of JSON objects.
* Encode from an {@code Object} stream to a byte stream of JSON objects,
* using Jackson 2.6+.
*
* @author Sebastien Deleuze
* @author Arjen Poutsma
* @since 5.0
* @see JacksonJsonDecoder
* @see Jackson2JsonDecoder
*/
public class JacksonJsonEncoder extends AbstractJacksonJsonCodec implements Encoder<Object> {
public class Jackson2JsonEncoder extends AbstractJackson2Codec implements Encoder<Object> {
private static final ByteBuffer START_ARRAY_BUFFER = ByteBuffer.wrap(new byte[]{'['});
@@ -57,14 +58,15 @@ public class JacksonJsonEncoder extends AbstractJacksonJsonCodec implements Enco
private static final ByteBuffer END_ARRAY_BUFFER = ByteBuffer.wrap(new byte[]{']'});
public JacksonJsonEncoder() {
public Jackson2JsonEncoder() {
super(Jackson2ObjectMapperBuilder.json().build());
}
public JacksonJsonEncoder(ObjectMapper mapper) {
public Jackson2JsonEncoder(ObjectMapper mapper) {
super(mapper);
}
@Override
public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (mimeType == null) {
@@ -105,38 +107,38 @@ public class JacksonJsonEncoder extends AbstractJacksonJsonCodec implements Enco
private DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory, ResolvableType type) {
TypeFactory typeFactory = this.mapper.getTypeFactory();
JavaType javaType = typeFactory.constructType(type.getType());
MethodParameter returnType = (type.getSource() instanceof MethodParameter ?
(MethodParameter)type.getSource() : null);
MethodParameter returnType =
(type.getSource() instanceof MethodParameter ? (MethodParameter) type.getSource() : null);
if (type != null && value != null && type.isAssignableFrom(value.getClass())) {
if (type.isInstance(value)) {
javaType = getJavaType(type.getType(), null);
}
ObjectWriter writer;
if (returnType != null && returnType.getMethodAnnotation(JsonView.class) != null) {
JsonView annotation = returnType.getMethodAnnotation(JsonView.class);
Class<?>[] classes = annotation.value();
ObjectWriter writer;
JsonView jsonView = (returnType != null ? returnType.getMethodAnnotation(JsonView.class) : null);
if (jsonView != null) {
Class<?>[] classes = jsonView.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for response body advice with exactly 1 class argument: " + returnType);
throw new IllegalArgumentException("@JsonView only supported for response body advice " +
"with exactly 1 class argument: " + returnType);
}
writer = this.mapper.writerWithView(classes[0]);
}
else {
writer = this.mapper.writer();
}
if (javaType != null && javaType.isContainerType()) {
writer = writer.forType(javaType);
}
DataBuffer buffer = bufferFactory.allocateBuffer();
OutputStream outputStream = buffer.asOutputStream();
try {
writer.writeValue(outputStream, value);
}
catch (IOException e) {
throw new CodecException("Error while writing the data", e);
catch (IOException ex) {
throw new CodecException("Error while writing the data", ex);
}
return buffer;

View File

@@ -31,7 +31,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractDecoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.support.DataBufferUtils;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.util.MimeType;
/**
@@ -109,17 +109,17 @@ class JsonObjectDecoder extends AbstractDecoder<DataBuffer> {
Integer writerIndex;
@Override
public Publisher<? extends DataBuffer> apply(DataBuffer b) {
public Publisher<? extends DataBuffer> apply(DataBuffer buffer) {
List<DataBuffer> chunks = new ArrayList<>();
if (this.input == null) {
this.input = Unpooled.copiedBuffer(b.asByteBuffer());
DataBufferUtils.release(b);
this.input = Unpooled.copiedBuffer(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
this.writerIndex = this.input.writerIndex();
}
else {
this.input = Unpooled.copiedBuffer(this.input,
Unpooled.copiedBuffer(b.asByteBuffer()));
DataBufferUtils.release(b);
Unpooled.copiedBuffer(buffer.asByteBuffer()));
DataBufferUtils.release(buffer);
this.writerIndex = this.input.writerIndex();
}
if (this.state == ST_CORRUPTED) {
@@ -133,7 +133,7 @@ class JsonObjectDecoder extends AbstractDecoder<DataBuffer> {
return Flux.error(new IllegalStateException("object length exceeds " +
maxObjectLength + ": " + this.writerIndex + " bytes discarded"));
}
DataBufferFactory dataBufferFactory = b.factory();
DataBufferFactory dataBufferFactory = buffer.factory();
for (/* use current index */; this.index < this.writerIndex; this.index++) {
byte c = this.input.getByte(this.index);
if (this.state == ST_DECODING_NORMAL) {

View File

@@ -50,9 +50,9 @@ import org.springframework.util.xml.StaxUtils;
* @author Sebastien Deleuze
* @author Arjen Poutsma
* @since 5.0
* @see Jaxb2Encoder
* @see Jaxb2XmlEncoder
*/
public class Jaxb2Decoder extends AbstractDecoder<Object> {
public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
/**
* The default value for JAXB annotations.
@@ -61,14 +61,15 @@ public class Jaxb2Decoder extends AbstractDecoder<Object> {
* @see XmlType#name()
* @see XmlType#namespace()
*/
private final static String JAXB_DEFAULT_ANNOTATION_VALUE = "##default";
private static final String JAXB_DEFAULT_ANNOTATION_VALUE = "##default";
private final XmlEventDecoder xmlEventDecoder = new XmlEventDecoder();
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
public Jaxb2Decoder() {
public Jaxb2XmlDecoder() {
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
}

View File

@@ -39,22 +39,24 @@ import org.springframework.util.MimeTypeUtils;
* @author Sebastien Deleuze
* @author Arjen Poutsma
* @since 5.0
* @see Jaxb2Decoder
* @see Jaxb2XmlDecoder
*/
public class Jaxb2Encoder extends AbstractSingleValueEncoder<Object> {
public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
public Jaxb2Encoder() {
public Jaxb2XmlEncoder() {
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
}
@Override
public boolean canEncode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (super.canEncode(elementType, mimeType, hints)) {
Class<?> outputClass = elementType.getRawClass();
return outputClass.isAnnotationPresent(XmlRootElement.class) ||
outputClass.isAnnotationPresent(XmlType.class);
return (outputClass.isAnnotationPresent(XmlRootElement.class) ||
outputClass.isAnnotationPresent(XmlType.class));
}
else {
return false;
@@ -80,7 +82,4 @@ public class Jaxb2Encoder extends AbstractSingleValueEncoder<Object> {
}
}
}

View File

@@ -38,7 +38,7 @@ import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractDecoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferUtils;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -65,30 +65,32 @@ import org.springframework.util.MimeTypeUtils;
* <li>{@link javax.xml.stream.events.EndElement} {@code root}</li>
* </ol>
*
* Note that this decoder is not registered by default, but used internally by other
* decoders who are.
* Note that this decoder is not registered by default but used internally
* by other decoders who are there by default.
*
* @author Arjen Poutsma
* @since 5.0
*/
public class XmlEventDecoder extends AbstractDecoder<XMLEvent> {
private static final boolean aaltoPresent = ClassUtils
.isPresent("com.fasterxml.aalto.AsyncXMLStreamReader",
XmlEventDecoder.class.getClassLoader());
private static final boolean aaltoPresent = ClassUtils.isPresent(
"com.fasterxml.aalto.AsyncXMLStreamReader", XmlEventDecoder.class.getClassLoader());
private static final XMLInputFactory inputFactory = XMLInputFactory.newFactory();
boolean useAalto = true;
public XmlEventDecoder() {
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
}
@Override
@SuppressWarnings("unchecked")
public Flux<XMLEvent> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
MimeType mimeType, Object... hints) {
Flux<DataBuffer> flux = Flux.from(inputStream);
if (useAalto && aaltoPresent) {
return flux.flatMap(new AaltoDataBufferToXmlEvent());
@@ -112,11 +114,11 @@ public class XmlEventDecoder extends AbstractDecoder<XMLEvent> {
}
}
/*
* Separate static class to isolate Aalto dependency.
*/
private static class AaltoDataBufferToXmlEvent
implements Function<DataBuffer, Publisher<? extends XMLEvent>> {
private static class AaltoDataBufferToXmlEvent implements Function<DataBuffer, Publisher<? extends XMLEvent>> {
private static final AsyncXMLInputFactory inputFactory = new InputFactoryImpl();
@@ -154,4 +156,5 @@ public class XmlEventDecoder extends AbstractDecoder<XMLEvent> {
}
}
}
}

View File

@@ -30,7 +30,7 @@ import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferUtils;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.util.Assert;
/**

View File

@@ -38,15 +38,15 @@ import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.http.codec.xml.Jaxb2Decoder;
import org.springframework.http.codec.xml.Jaxb2Encoder;
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ResourceHttpMessageWriter;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
import org.springframework.util.ClassUtils;
/**
@@ -106,8 +106,8 @@ public final class WebClient {
* <ul>
* <li>{@link ByteBufferEncoder} / {@link ByteBufferDecoder}</li>
* <li>{@link CharSequenceEncoder} / {@link StringDecoder}</li>
* <li>{@link Jaxb2Encoder} / {@link Jaxb2Decoder}</li>
* <li>{@link JacksonJsonEncoder} / {@link JacksonJsonDecoder}</li>
* <li>{@link Jaxb2XmlEncoder} / {@link Jaxb2XmlDecoder}</li>
* <li>{@link Jackson2JsonEncoder} / {@link Jackson2JsonDecoder}</li>
* </ul>
*
* @param clientHttpConnector the {@code ClientHttpRequestFactory} to use
@@ -126,10 +126,10 @@ public final class WebClient {
messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder(false)));
messageReaders.add(new DecoderHttpMessageReader<>(new ResourceDecoder()));
if (jaxb2Present) {
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2Decoder()));
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
}
if (jackson2Present) {
messageReaders.add(new DecoderHttpMessageReader<>(new JacksonJsonDecoder()));
messageReaders.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
}
}
@@ -141,10 +141,10 @@ public final class WebClient {
messageWriters.add(new EncoderHttpMessageWriter<>(new CharSequenceEncoder()));
messageWriters.add(new ResourceHttpMessageWriter());
if (jaxb2Present) {
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
}
if (jackson2Present) {
messageWriters.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));
messageWriters.add(new EncoderHttpMessageWriter<>(new Jackson2JsonEncoder()));
}
}