Merge branch '5.1.x'

This commit is contained in:
Juergen Hoeller
2019-03-05 14:20:02 +01:00
58 changed files with 1929 additions and 2139 deletions

View File

@@ -86,7 +86,8 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
public Flux<Object> decode(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(Flux.from(input), this.jsonFactory, true);
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
Flux.from(input), this.jsonFactory, getObjectMapper().getDeserializationContext(), true);
return decodeInternal(tokens, elementType, mimeType, hints);
}
@@ -94,7 +95,8 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(Flux.from(input), this.jsonFactory, false);
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
Flux.from(input), this.jsonFactory, getObjectMapper().getDeserializationContext(), false);
return decodeInternal(tokens, elementType, mimeType, hints).singleOrEmpty();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -119,7 +119,7 @@ public abstract class Jackson2CodecSupport {
@Nullable
protected MethodParameter getParameter(ResolvableType type) {
return type.getSource() instanceof MethodParameter ? (MethodParameter) type.getSource() : null;
return (type.getSource() instanceof MethodParameter ? (MethodParameter) type.getSource() : null);
}
@Nullable

View File

@@ -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.
@@ -26,13 +26,13 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import reactor.core.publisher.Flux;
import org.springframework.core.codec.DecodingException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.util.Assert;
/**
* {@link Function} to transform a JSON stream of arbitrary size, byte array
@@ -40,12 +40,16 @@ import org.springframework.util.Assert;
* well-formed JSON object.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 5.0
*/
final class Jackson2Tokenizer {
private final JsonParser parser;
private final DeserializationContext deserializationContext;
private final boolean tokenizeArrayElements;
private TokenBuffer tokenBuffer;
@@ -59,36 +63,16 @@ final class Jackson2Tokenizer {
private final ByteArrayFeeder inputFeeder;
private Jackson2Tokenizer(JsonParser parser, boolean tokenizeArrayElements) {
Assert.notNull(parser, "'parser' must not be null");
private Jackson2Tokenizer(
JsonParser parser, DeserializationContext deserializationContext, boolean tokenizeArrayElements) {
this.parser = parser;
this.deserializationContext = deserializationContext;
this.tokenizeArrayElements = tokenizeArrayElements;
this.tokenBuffer = new TokenBuffer(parser);
this.tokenBuffer = new TokenBuffer(parser, deserializationContext);
this.inputFeeder = (ByteArrayFeeder) this.parser.getNonBlockingInputFeeder();
}
/**
* Tokenize the given {@code Flux<DataBuffer>} into {@code Flux<TokenBuffer>}.
* @param dataBuffers the source data buffers
* @param jsonFactory the factory to use
* @param tokenizeArrayElements if {@code true} and the "top level" JSON
* object is an array, each element is returned individually, immediately
* after it is received.
* @return the result token buffers
*/
public static Flux<TokenBuffer> tokenize(Flux<DataBuffer> dataBuffers, JsonFactory jsonFactory,
boolean tokenizeArrayElements) {
try {
JsonParser parser = jsonFactory.createNonBlockingByteArrayParser();
Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, tokenizeArrayElements);
return dataBuffers.flatMap(tokenizer::tokenize, Flux::error, tokenizer::endOfInput);
}
catch (IOException ex) {
return Flux.error(ex);
}
}
private Flux<TokenBuffer> tokenize(DataBuffer dataBuffer) {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
@@ -100,8 +84,7 @@ final class Jackson2Tokenizer {
return parseTokenBufferFlux();
}
catch (JsonProcessingException ex) {
return Flux.error(new DecodingException(
"JSON decoding error: " + ex.getOriginalMessage(), ex));
return Flux.error(new DecodingException("JSON decoding error: " + ex.getOriginalMessage(), ex));
}
catch (IOException ex) {
return Flux.error(ex);
@@ -114,8 +97,7 @@ final class Jackson2Tokenizer {
return parseTokenBufferFlux();
}
catch (JsonProcessingException ex) {
return Flux.error(new DecodingException(
"JSON decoding error: " + ex.getOriginalMessage(), ex));
return Flux.error(new DecodingException("JSON decoding error: " + ex.getOriginalMessage(), ex));
}
catch (IOException ex) {
return Flux.error(ex);
@@ -128,12 +110,11 @@ final class Jackson2Tokenizer {
while (true) {
JsonToken token = this.parser.nextToken();
// SPR-16151: Smile data format uses null to separate documents
if ((token == JsonToken.NOT_AVAILABLE) ||
if (token == JsonToken.NOT_AVAILABLE ||
(token == null && (token = this.parser.nextToken()) == null)) {
break;
}
updateDepth(token);
if (!this.tokenizeArrayElements) {
processTokenNormal(token, result);
}
@@ -164,10 +145,9 @@ final class Jackson2Tokenizer {
private void processTokenNormal(JsonToken token, List<TokenBuffer> result) throws IOException {
this.tokenBuffer.copyCurrentEvent(this.parser);
if ((token.isStructEnd() || token.isScalarValue()) &&
this.objectDepth == 0 && this.arrayDepth == 0) {
if ((token.isStructEnd() || token.isScalarValue()) && this.objectDepth == 0 && this.arrayDepth == 0) {
result.add(this.tokenBuffer);
this.tokenBuffer = new TokenBuffer(this.parser);
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
}
}
@@ -177,11 +157,10 @@ final class Jackson2Tokenizer {
this.tokenBuffer.copyCurrentEvent(this.parser);
}
if (this.objectDepth == 0 &&
(this.arrayDepth == 0 || this.arrayDepth == 1) &&
if (this.objectDepth == 0 && (this.arrayDepth == 0 || this.arrayDepth == 1) &&
(token == JsonToken.END_OBJECT || token.isScalarValue())) {
result.add(this.tokenBuffer);
this.tokenBuffer = new TokenBuffer(this.parser);
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
}
}
@@ -190,4 +169,26 @@ final class Jackson2Tokenizer {
(token == JsonToken.END_ARRAY && this.arrayDepth == 0));
}
/**
* Tokenize the given {@code Flux<DataBuffer>} into {@code Flux<TokenBuffer>}.
* @param dataBuffers the source data buffers
* @param jsonFactory the factory to use
* @param tokenizeArrayElements if {@code true} and the "top level" JSON object is
* an array, each element is returned individually immediately after it is received
* @return the resulting token buffers
*/
public static Flux<TokenBuffer> tokenize(Flux<DataBuffer> dataBuffers, JsonFactory jsonFactory,
DeserializationContext deserializationContext, boolean tokenizeArrayElements) {
try {
JsonParser parser = jsonFactory.createNonBlockingByteArrayParser();
Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, deserializationContext, tokenizeArrayElements);
return dataBuffers.flatMap(tokenizer::tokenize, Flux::error, tokenizer::endOfInput);
}
catch (IOException ex) {
return Flux.error(ex);
}
}
}

View File

@@ -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.
@@ -39,11 +39,10 @@ import org.springframework.web.context.ServletContextAware;
/**
* Subclass of {@link GenericApplicationContext}, suitable for web environments.
*
* <p>Implements the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext},
* but is not intended for declarative setup in {@code web.xml}. Instead,
* it is designed for programmatic setup, for example for building nested contexts or
* for use within Spring 3.1 {@link org.springframework.web.WebApplicationInitializer org.springframework.web.WebApplicationInitializers}.
* <p>Implements {@link org.springframework.web.context.ConfigurableWebApplicationContext},
* but is not intended for declarative setup in {@code web.xml}. Instead, it is designed
* for programmatic setup, for example for building nested contexts or for use within
* {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializers}.
*
* <p><b>If you intend to implement a WebApplicationContext that reads bean definitions
* from configuration files, consider deriving from AbstractRefreshableWebApplicationContext,