Correlated encoding/decoding log messages via hints
Issue: SPR-16966
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.core.codec;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -60,17 +59,4 @@ public abstract class AbstractEncoder<T> implements Encoder<T> {
|
||||
return this.encodableMimeTypes.stream().anyMatch(candidate -> candidate.isCompatibleWith(mimeType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to obtain the logger to use from the Map of hints, or fall
|
||||
* back on the default logger. This may be used for example to override
|
||||
* logging, e.g. for a multipart request where the full map of part values
|
||||
* has already been logged.
|
||||
* @param hints the hints passed to the encode method
|
||||
* @return the logger to use
|
||||
* @since 5.1
|
||||
*/
|
||||
protected Log getLogger(@Nullable Map<String, Object> hints) {
|
||||
return hints != null ? ((Log) hints.getOrDefault(Log.class.getName(), logger)) : logger;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ByteArrayDecoder extends AbstractDataBufferDecoder<byte[]> {
|
||||
dataBuffer.read(result);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Read " + result.length + " bytes");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Read " + result.length + " bytes");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -55,9 +55,10 @@ public class ByteArrayEncoder extends AbstractEncoder<byte[]> {
|
||||
|
||||
return Flux.from(inputStream).map(bytes -> {
|
||||
DataBuffer dataBuffer = bufferFactory.wrap(bytes);
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Writing " + dataBuffer.readableByteCount() + " bytes");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) +
|
||||
"Writing " + dataBuffer.readableByteCount() + " bytes");
|
||||
}
|
||||
return dataBuffer;
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ByteBufferDecoder extends AbstractDataBufferDecoder<ByteBuffer> {
|
||||
copy.flip();
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Read " + byteCount + " bytes");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Read " + byteCount + " bytes");
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -56,9 +56,10 @@ public class ByteBufferEncoder extends AbstractEncoder<ByteBuffer> {
|
||||
|
||||
return Flux.from(inputStream).map(byteBuffer -> {
|
||||
DataBuffer dataBuffer = bufferFactory.wrap(byteBuffer);
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Writing " + dataBuffer.readableByteCount() + " bytes");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) +
|
||||
"Writing " + dataBuffer.readableByteCount() + " bytes");
|
||||
}
|
||||
return dataBuffer;
|
||||
});
|
||||
|
||||
@@ -69,9 +69,9 @@ public final class CharSequenceEncoder extends AbstractEncoder<CharSequence> {
|
||||
Charset charset = getCharset(mimeType);
|
||||
|
||||
return Flux.from(inputStream).map(charSequence -> {
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Writing '" + charSequence + "'");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) + "Writing '" + charSequence + "'");
|
||||
}
|
||||
CharBuffer charBuffer = CharBuffer.wrap(charSequence);
|
||||
ByteBuffer byteBuffer = charset.encode(charBuffer);
|
||||
|
||||
@@ -63,7 +63,7 @@ public class DataBufferDecoder extends AbstractDataBufferDecoder<DataBuffer> {
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Read " + buffer.readableByteCount() + " bytes");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Read " + buffer.readableByteCount() + " bytes");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -55,9 +55,11 @@ public class DataBufferEncoder extends AbstractEncoder<DataBuffer> {
|
||||
|
||||
Flux<DataBuffer> flux = Flux.from(inputStream);
|
||||
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
flux = flux.doOnNext(buffer -> logger.debug("Writing " + buffer.readableByteCount() + " bytes"));
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
flux = flux.doOnNext(buffer ->
|
||||
theLogger.debug(Hints.getLogPrefix(hints) +
|
||||
"Writing " + buffer.readableByteCount() + " bytes"));
|
||||
}
|
||||
|
||||
return flux;
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.core.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Constants and convenience methods for working with hints.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.1
|
||||
* @see ResourceRegionEncoder#BOUNDARY_STRING_HINT
|
||||
*/
|
||||
public abstract class Hints {
|
||||
|
||||
/**
|
||||
* Name of hint exposing a prefix to use for correlating log messages.
|
||||
* @since 5.1
|
||||
*/
|
||||
public static final String LOG_PREFIX_HINT = Log.class.getName() + ".PREFIX";
|
||||
|
||||
/**
|
||||
* Name of hint for a preferred {@link Log logger} to use. This can be used
|
||||
* by a composite encoder (e.g. multipart requests) to control or suppress
|
||||
* logging by individual part encoders.
|
||||
* @since 5.1
|
||||
*/
|
||||
public static final String LOGGER_HINT = Log.class.getName();
|
||||
|
||||
|
||||
/**
|
||||
* Create a map wit a single hint via {@link Collections#singletonMap}.
|
||||
* @param hintName the hint name
|
||||
* @param value the hint value
|
||||
* @return the created map
|
||||
*/
|
||||
public static Map<String, Object> from(String hintName, Object value) {
|
||||
return Collections.singletonMap(hintName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an empty map of hints via {@link Collections#emptyMap()}.
|
||||
* @return the empty map
|
||||
*/
|
||||
public static Map<String, Object> none() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the value for a required hint.
|
||||
* @param hints the hints map
|
||||
* @param hintName the required hint name
|
||||
* @param <T> the hint type to cast to
|
||||
* @return the hint value
|
||||
* @throws IllegalArgumentException if the hint is not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getRequiredHint(@Nullable Map<String, Object> hints, String hintName) {
|
||||
if (hints == null) {
|
||||
throw new IllegalArgumentException("No hints map for required hint '" + hintName + "'");
|
||||
}
|
||||
T hint = (T) hints.get(hintName);
|
||||
if (hint == null) {
|
||||
throw new IllegalArgumentException("Hints map must contain the hint '" + hintName + "'");
|
||||
}
|
||||
return hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the hint {@link #LOG_PREFIX_HINT}, if present, or an empty String.
|
||||
* @param hints the hints passed to the encode method
|
||||
* @return the log prefix
|
||||
*/
|
||||
public static String getLogPrefix(@Nullable Map<String, Object> hints) {
|
||||
return hints != null ? (String) hints.getOrDefault(LOG_PREFIX_HINT, "") : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the hint {@link #LOGGER_HINT}, if present, or the given logger.
|
||||
* @param hints the hints passed to the encode method
|
||||
* @param defaultLogger the logger to return if a hint is not found
|
||||
* @return the logger to use
|
||||
*/
|
||||
public static Log getLoggerOrDefault(@Nullable Map<String, Object> hints, Log defaultLogger) {
|
||||
return hints != null ? (Log) hints.getOrDefault(LOGGER_HINT, defaultLogger) : defaultLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two maps of hints, creating and copying into a new map if both have
|
||||
* values, or returning the non-empty map, or an empty map if both are empty.
|
||||
* @param hints1 1st map of hints
|
||||
* @param hints2 2nd map of hints
|
||||
* @return a single map with hints from both
|
||||
*/
|
||||
public static Map<String, Object> merge(Map<String, Object> hints1, Map<String, Object> hints2) {
|
||||
if (hints1.isEmpty() && hints2.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
else if (hints2.isEmpty()) {
|
||||
return hints1;
|
||||
}
|
||||
else if (hints1.isEmpty()) {
|
||||
return hints2;
|
||||
}
|
||||
else {
|
||||
Map<String, Object> result = new HashMap<>(hints1.size() + hints2.size());
|
||||
result.putAll(hints1);
|
||||
result.putAll(hints2);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a single hint into a map of hints, possibly creating and copying
|
||||
* all hints into a new map, or otherwise if the map of hints is empty,
|
||||
* creating a new single entry map.
|
||||
* @param hints a map of hints to be merge
|
||||
* @param hintName the hint name to merge
|
||||
* @param hintValue the hint value to merge
|
||||
* @return a single map with all hints
|
||||
*/
|
||||
public static Map<String, Object> merge(Map<String, Object> hints, String hintName, Object hintValue) {
|
||||
if (hints.isEmpty()) {
|
||||
return Collections.singletonMap(hintName, hintValue);
|
||||
}
|
||||
else {
|
||||
Map<String, Object> result = new HashMap<>(hints.size() + 1);
|
||||
result.putAll(hints);
|
||||
result.put(hintName, hintValue);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
Assert.state(clazz != null, "No resource class");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Read " + bytes.length + " bytes");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Read " + bytes.length + " bytes");
|
||||
}
|
||||
|
||||
if (InputStreamResource.class == clazz) {
|
||||
|
||||
@@ -69,9 +69,9 @@ public class ResourceEncoder extends AbstractSingleValueEncoder<Resource> {
|
||||
protected Flux<DataBuffer> encode(Resource resource, DataBufferFactory dataBufferFactory,
|
||||
ResolvableType type, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Writing [" + resource + "]");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) + "Writing [" + resource + "]");
|
||||
}
|
||||
|
||||
return DataBufferUtils.read(resource, dataBufferFactory, this.bufferSize);
|
||||
|
||||
@@ -91,10 +91,7 @@ public class ResourceRegionEncoder extends AbstractEncoder<ResourceRegion> {
|
||||
.flatMapMany(region -> writeResourceRegion(region, bufferFactory, hints));
|
||||
}
|
||||
else {
|
||||
Assert.notNull(hints, "'hints' must not be null");
|
||||
Assert.isTrue(hints.containsKey(BOUNDARY_STRING_HINT), "'hints' must contain boundaryString hint");
|
||||
final String boundaryString = (String) hints.get(BOUNDARY_STRING_HINT);
|
||||
|
||||
final String boundaryString = Hints.getRequiredHint(hints, BOUNDARY_STRING_HINT);
|
||||
byte[] startBoundary = getAsciiBytes("\r\n--" + boundaryString + "\r\n");
|
||||
byte[] contentType =
|
||||
(mimeType != null ? getAsciiBytes("Content-Type: " + mimeType + "\r\n") : new byte[0]);
|
||||
@@ -126,9 +123,10 @@ public class ResourceRegionEncoder extends AbstractEncoder<ResourceRegion> {
|
||||
long position = region.getPosition();
|
||||
long count = region.getCount();
|
||||
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Writing region " + position + "-" + (position + count) + " of [" + resource + "]");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) +
|
||||
"Writing region " + position + "-" + (position + count) + " of [" + resource + "]");
|
||||
}
|
||||
|
||||
Flux<DataBuffer> in = DataBufferUtils.read(resource, position, bufferFactory, this.bufferSize);
|
||||
@@ -149,7 +147,8 @@ public class ResourceRegionEncoder extends AbstractEncoder<ResourceRegion> {
|
||||
long end = start + region.getCount() - 1;
|
||||
OptionalLong contentLength = contentLength(region.getResource());
|
||||
if (contentLength.isPresent()) {
|
||||
return getAsciiBytes("Content-Range: bytes " + start + '-' + end + '/' + contentLength.getAsLong() + "\r\n\r\n");
|
||||
long length = contentLength.getAsLong();
|
||||
return getAsciiBytes("Content-Range: bytes " + start + '-' + end + '/' + length + "\r\n\r\n");
|
||||
}
|
||||
else {
|
||||
return getAsciiBytes("Content-Range: bytes " + start + '-' + end + "\r\n\r\n");
|
||||
|
||||
@@ -207,7 +207,7 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
String value = charBuffer.toString();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decoded '" + value + "'");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Decoded '" + value + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -26,6 +24,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.HttpMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
@@ -114,9 +113,8 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
public Flux<T> read(ResolvableType actualType, ResolvableType elementType,
|
||||
ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) {
|
||||
|
||||
Map<String, Object> allHints = new HashMap<>(4);
|
||||
allHints.putAll(getReadHints(actualType, elementType, request, response));
|
||||
allHints.putAll(hints);
|
||||
Map<String, Object> allHints = Hints.merge(hints,
|
||||
getReadHints(actualType, elementType, request, response));
|
||||
|
||||
return read(elementType, request, allHints);
|
||||
}
|
||||
@@ -125,9 +123,8 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
public Mono<T> readMono(ResolvableType actualType, ResolvableType elementType,
|
||||
ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) {
|
||||
|
||||
Map<String, Object> allHints = new HashMap<>(4);
|
||||
allHints.putAll(getReadHints(actualType, elementType, request, response));
|
||||
allHints.putAll(hints);
|
||||
Map<String, Object> allHints = Hints.merge(hints,
|
||||
getReadHints(actualType, elementType, request, response));
|
||||
|
||||
return readMono(elementType, request, allHints);
|
||||
}
|
||||
@@ -141,10 +138,10 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.decoder instanceof HttpMessageDecoder) {
|
||||
HttpMessageDecoder<?> httpDecoder = (HttpMessageDecoder<?>) this.decoder;
|
||||
return httpDecoder.getDecodeHints(actualType, elementType, request, response);
|
||||
HttpMessageDecoder<?> decoder = (HttpMessageDecoder<?>) this.decoder;
|
||||
return decoder.getDecodeHints(actualType, elementType, request, response);
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -27,6 +25,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -159,9 +158,8 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
ResolvableType elementType, @Nullable MediaType mediaType, ServerHttpRequest request,
|
||||
ServerHttpResponse response, Map<String, Object> hints) {
|
||||
|
||||
Map<String, Object> allHints = new HashMap<>();
|
||||
allHints.putAll(getWriteHints(actualType, elementType, mediaType, request, response));
|
||||
allHints.putAll(hints);
|
||||
Map<String, Object> allHints = Hints.merge(hints,
|
||||
getWriteHints(actualType, elementType, mediaType, request, response));
|
||||
|
||||
return write(inputStream, elementType, mediaType, response, allHints);
|
||||
}
|
||||
@@ -175,10 +173,10 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
@Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.encoder instanceof HttpMessageEncoder) {
|
||||
HttpMessageEncoder<?> httpEncoder = (HttpMessageEncoder<?>) this.encoder;
|
||||
return httpEncoder.getEncodeHints(streamType, elementType, mediaType, request, response);
|
||||
HttpMessageEncoder<?> encoder = (HttpMessageEncoder<?>) this.encoder;
|
||||
return encoder.getEncodeHints(streamType, elementType, mediaType, request, response);
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
@@ -108,7 +109,7 @@ public class FormHttpMessageReader extends LoggingCodecSupport
|
||||
DataBufferUtils.release(buffer);
|
||||
MultiValueMap<String, String> formData = parseFormData(charset, body);
|
||||
if (shouldLogRequestDetails()) {
|
||||
logger.debug("Decoded " + formData);
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Decoded " + formData);
|
||||
}
|
||||
return formData;
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
@@ -131,7 +132,7 @@ public class FormHttpMessageWriter extends LoggingCodecSupport
|
||||
|
||||
return Mono.from(inputStream).flatMap(form -> {
|
||||
if (shouldLogRequestDetails()) {
|
||||
logger.debug("Encoding " + form);
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Encoding " + form);
|
||||
}
|
||||
String value = serializeForm(form, charset);
|
||||
ByteBuffer byteBuffer = charset.encode(value);
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
@@ -58,7 +58,7 @@ public interface HttpMessageEncoder<T> extends Encoder<T> {
|
||||
default Map<String, Object> getEncodeHints(ResolvableType actualType, ResolvableType elementType,
|
||||
@Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.http.codec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -31,6 +29,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.codec.ResourceDecoder;
|
||||
import org.springframework.core.codec.ResourceEncoder;
|
||||
import org.springframework.core.codec.ResourceRegionEncoder;
|
||||
@@ -119,7 +118,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
ReactiveHttpOutputMessage message, Map<String, Object> hints) {
|
||||
|
||||
HttpHeaders headers = message.getHeaders();
|
||||
MediaType resourceMediaType = getResourceMediaType(mediaType, resource);
|
||||
MediaType resourceMediaType = getResourceMediaType(mediaType, resource, hints);
|
||||
headers.setContentType(resourceMediaType);
|
||||
|
||||
if (headers.getContentLength() < 0) {
|
||||
@@ -129,7 +128,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
}
|
||||
}
|
||||
|
||||
return zeroCopy(resource, null, message)
|
||||
return zeroCopy(resource, null, message, hints)
|
||||
.orElseGet(() -> {
|
||||
Mono<Resource> input = Mono.just(resource);
|
||||
DataBufferFactory factory = message.bufferFactory();
|
||||
@@ -138,13 +137,15 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
});
|
||||
}
|
||||
|
||||
private static MediaType getResourceMediaType(@Nullable MediaType mediaType, Resource resource) {
|
||||
private static MediaType getResourceMediaType(
|
||||
@Nullable MediaType mediaType, Resource resource, Map<String, Object> hints) {
|
||||
|
||||
if (mediaType != null && mediaType.isConcrete() && !mediaType.equals(MediaType.APPLICATION_OCTET_STREAM)) {
|
||||
return mediaType;
|
||||
}
|
||||
mediaType = MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource associated with '" + mediaType + "'");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Resource associated with '" + mediaType + "'");
|
||||
}
|
||||
return mediaType;
|
||||
}
|
||||
@@ -162,7 +163,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
}
|
||||
|
||||
private static Optional<Mono<Void>> zeroCopy(Resource resource, @Nullable ResourceRegion region,
|
||||
ReactiveHttpOutputMessage message) {
|
||||
ReactiveHttpOutputMessage message, Map<String, Object> hints) {
|
||||
|
||||
if (message instanceof ZeroCopyHttpOutputMessage && resource.isFile()) {
|
||||
try {
|
||||
@@ -171,7 +172,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
long count = region != null ? region.getCount() : file.length();
|
||||
if (logger.isDebugEnabled()) {
|
||||
String formatted = region != null ? "region " + pos + "-" + (count) + " of " : "";
|
||||
logger.debug("Zero-copy " + formatted + "[" + resource + "]");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Zero-copy " + formatted + "[" + resource + "]");
|
||||
}
|
||||
return Optional.of(((ZeroCopyHttpOutputMessage) message).writeWith(file, pos, count));
|
||||
}
|
||||
@@ -211,7 +212,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
|
||||
response.setStatusCode(HttpStatus.PARTIAL_CONTENT);
|
||||
List<ResourceRegion> regions = HttpRange.toResourceRegions(ranges, resource);
|
||||
MediaType resourceMediaType = getResourceMediaType(mediaType, resource);
|
||||
MediaType resourceMediaType = getResourceMediaType(mediaType, resource, hints);
|
||||
|
||||
if (regions.size() == 1){
|
||||
ResourceRegion region = regions.get(0);
|
||||
@@ -224,26 +225,26 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
headers.add("Content-Range", "bytes " + start + '-' + end + '/' + contentLength);
|
||||
headers.setContentLength(end - start + 1);
|
||||
}
|
||||
return writeSingleRegion(region, response);
|
||||
return writeSingleRegion(region, response, hints);
|
||||
}
|
||||
else {
|
||||
String boundary = MimeTypeUtils.generateMultipartBoundaryString();
|
||||
MediaType multipartType = MediaType.parseMediaType("multipart/byteranges;boundary=" + boundary);
|
||||
headers.setContentType(multipartType);
|
||||
Map<String, Object> theHints = new HashMap<>(hints);
|
||||
theHints.put(ResourceRegionEncoder.BOUNDARY_STRING_HINT, boundary);
|
||||
return encodeAndWriteRegions(Flux.fromIterable(regions), resourceMediaType, response, theHints);
|
||||
Map<String, Object> allHints = Hints.merge(hints, ResourceRegionEncoder.BOUNDARY_STRING_HINT, boundary);
|
||||
return encodeAndWriteRegions(Flux.fromIterable(regions), resourceMediaType, response, allHints);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Void> writeSingleRegion(ResourceRegion region, ReactiveHttpOutputMessage message) {
|
||||
private Mono<Void> writeSingleRegion(ResourceRegion region, ReactiveHttpOutputMessage message,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
return zeroCopy(region.getResource(), region, message)
|
||||
return zeroCopy(region.getResource(), region, message, hints)
|
||||
.orElseGet(() -> {
|
||||
Publisher<? extends ResourceRegion> input = Mono.just(region);
|
||||
MediaType mediaType = message.getHeaders().getContentType();
|
||||
return encodeAndWriteRegions(input, mediaType, message, Collections.emptyMap());
|
||||
return encodeAndWriteRegions(input, mediaType, message, hints);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
boolean shouldWrap = isServerSentEvent(elementType);
|
||||
ResolvableType valueType = (shouldWrap ? elementType.getGeneric() : elementType);
|
||||
|
||||
return stringDecoder.decode(message.getBody(), STRING_TYPE, null, Collections.emptyMap())
|
||||
return stringDecoder.decode(message.getBody(), STRING_TYPE, null, hints)
|
||||
.bufferUntil(line -> line.equals(""))
|
||||
.concatMap(lines -> buildEvent(lines, valueType, shouldWrap, hints));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.http.codec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -30,6 +29,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -188,9 +188,8 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
@Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
Map<String, Object> allHints = new HashMap<>();
|
||||
allHints.putAll(getEncodeHints(actualType, elementType, mediaType, request, response));
|
||||
allHints.putAll(hints);
|
||||
Map<String, Object> allHints = Hints.merge(hints,
|
||||
getEncodeHints(actualType, elementType, mediaType, request, response));
|
||||
|
||||
return write(input, elementType, mediaType, response, allHints);
|
||||
}
|
||||
@@ -199,10 +198,10 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
@Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.encoder instanceof HttpMessageEncoder) {
|
||||
HttpMessageEncoder<?> httpEncoder = (HttpMessageEncoder<?>) this.encoder;
|
||||
return httpEncoder.getEncodeHints(actualType, elementType, mediaType, request, response);
|
||||
HttpMessageEncoder<?> encoder = (HttpMessageEncoder<?>) this.encoder;
|
||||
return encoder.getEncodeHints(actualType, elementType, mediaType, request, response);
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.codec.HttpMessageDecoder;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -112,7 +113,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||
try {
|
||||
Object value = reader.readValue(tokenBuffer.asParser(getObjectMapper()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decoded [" + value + "]");
|
||||
logger.debug(Hints.getLogPrefix(hints) +"Decoded [" + value + "]");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.EncodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -141,9 +142,9 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBufferFactory bufferFactory,
|
||||
ResolvableType elementType, @Nullable Map<String, Object> hints, JsonEncoding encoding) {
|
||||
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Encoding [" + value + "]");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) + "Encoding [" + value + "]");
|
||||
}
|
||||
|
||||
JavaType javaType = getJavaType(elementType.getType(), null);
|
||||
@@ -218,7 +219,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
public Map<String, Object> getEncodeHints(@Nullable ResolvableType actualType, ResolvableType elementType,
|
||||
@Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
return (actualType != null ? getHints(actualType) : Collections.emptyMap());
|
||||
return (actualType != null ? getHints(actualType) : Hints.none());
|
||||
}
|
||||
|
||||
// Jackson2CodecSupport ...
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -98,19 +99,6 @@ public abstract class Jackson2CodecSupport {
|
||||
return (mimeType == null || this.mimeTypes.stream().anyMatch(m -> m.isCompatibleWith(mimeType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to obtain the logger to use from the Map of hints, or fall
|
||||
* back on the default logger. This may be used for example to override
|
||||
* logging, e.g. for a multipart request where the full map of part values
|
||||
* has already been logged.
|
||||
* @param hints the hints passed to the encode method
|
||||
* @return the logger to use
|
||||
* @since 5.1
|
||||
*/
|
||||
protected Log getLogger(@Nullable Map<String, Object> hints) {
|
||||
return hints != null ? ((Log) hints.getOrDefault(Log.class.getName(), logger)) : logger;
|
||||
}
|
||||
|
||||
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
|
||||
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
|
||||
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
|
||||
@@ -123,10 +111,10 @@ public abstract class Jackson2CodecSupport {
|
||||
if (annotation != null) {
|
||||
Class<?>[] classes = annotation.value();
|
||||
Assert.isTrue(classes.length == 1, JSON_VIEW_HINT_ERROR + param);
|
||||
return Collections.singletonMap(JSON_VIEW_HINT, classes[0]);
|
||||
return Hints.from(JSON_VIEW_HINT, classes[0]);
|
||||
}
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.impl.NoOpLog;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -38,6 +37,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
@@ -87,8 +87,7 @@ public class MultipartHttpMessageWriter extends LoggingCodecSupport
|
||||
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
/** Suppress logging from individual part writers (full map logged at this level). */
|
||||
private static final Map<String, Object> DEFAULT_HINTS =
|
||||
Collections.singletonMap(Log.class.getName(), new NoOpLog());
|
||||
private static final Map<String, Object> DEFAULT_HINTS = Hints.from(Hints.LOGGER_HINT, new NoOpLog());
|
||||
|
||||
|
||||
private final List<HttpMessageWriter<?>> partWriters;
|
||||
@@ -191,7 +190,7 @@ public class MultipartHttpMessageWriter extends LoggingCodecSupport
|
||||
|
||||
return Mono.from(inputStream).flatMap(map -> {
|
||||
if (this.formWriter == null || isMultipart(map, mediaType)) {
|
||||
return writeMultipart(map, outputMessage);
|
||||
return writeMultipart(map, outputMessage, hints);
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -216,7 +215,9 @@ public class MultipartHttpMessageWriter extends LoggingCodecSupport
|
||||
return false;
|
||||
}
|
||||
|
||||
private Mono<Void> writeMultipart(MultiValueMap<String, ?> map, ReactiveHttpOutputMessage outputMessage) {
|
||||
private Mono<Void> writeMultipart(
|
||||
MultiValueMap<String, ?> map, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
|
||||
|
||||
byte[] boundary = generateMultipartBoundary();
|
||||
|
||||
Map<String, String> params = new HashMap<>(2);
|
||||
@@ -226,7 +227,7 @@ public class MultipartHttpMessageWriter extends LoggingCodecSupport
|
||||
outputMessage.getHeaders().setContentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params));
|
||||
|
||||
if (shouldLogRequestDetails()) {
|
||||
logger.debug("Encoding " + map);
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Encoding " + map);
|
||||
}
|
||||
|
||||
Flux<DataBuffer> body = Flux.fromIterable(map.entrySet())
|
||||
|
||||
@@ -45,6 +45,7 @@ import reactor.core.publisher.FluxSink;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
@@ -95,7 +96,7 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
return Flux.create(new SynchronossPartGenerator(message, this.bufferFactory, this.streamStorageFactory))
|
||||
.doOnNext(part -> {
|
||||
if (shouldLogRequestDetails()) {
|
||||
logger.debug("Decoded [" + part + "]");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Decoded [" + part + "]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.AbstractDecoder;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -107,7 +108,7 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
|
||||
return splitEvents.map(events -> {
|
||||
Object value = unmarshal(events, outputClass);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decoded [" + value + "]");
|
||||
logger.debug(Hints.getLogPrefix(hints) + "Decoded [" + value + "]");
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.AbstractSingleValueEncoder;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.EncodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -74,9 +75,9 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
|
||||
protected Flux<DataBuffer> encode(Object value, DataBufferFactory dataBufferFactory,
|
||||
ResolvableType type, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
try {
|
||||
Log logger = getLogger(hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Encoding [" + value + "]");
|
||||
Log theLogger = Hints.getLoggerOrDefault(hints, logger);
|
||||
if (theLogger.isDebugEnabled()) {
|
||||
theLogger.debug(Hints.getLogPrefix(hints) + "Encoding [" + value + "]");
|
||||
}
|
||||
DataBuffer buffer = dataBufferFactory.allocateBuffer(1024);
|
||||
OutputStream outputStream = buffer.asOutputStream();
|
||||
|
||||
@@ -44,9 +44,9 @@ import org.springframework.util.MultiValueMap;
|
||||
public interface ServerWebExchange {
|
||||
|
||||
/**
|
||||
* Name of {@link #getAttributes() attribute} whose value correlates log
|
||||
* messages for the exchange. Use {@link #getLogPrefix()} to obtain a
|
||||
* consistently formatted prefix based on this attribute.
|
||||
* Name of {@link #getAttributes() attribute} whose value can be used to
|
||||
* correlate log messages for this exchange. Use {@link #getLogPrefix()} to
|
||||
* obtain a consistently formatted prefix based on this attribute.
|
||||
* @since 5.1
|
||||
* @see #getLogPrefix()
|
||||
*/
|
||||
@@ -219,9 +219,10 @@ public interface ServerWebExchange {
|
||||
void addUrlTransformer(Function<String, String> transformer);
|
||||
|
||||
/**
|
||||
* Return a common prefix to use for log messages related to the exchange.
|
||||
* The prefix is based on the value of the {@link #LOG_ID_ATTRIBUTE}
|
||||
* attribute along with some extra formatting.
|
||||
* Return a log message prefix to use to correlate messages for this exchange.
|
||||
* The prefix is based on the value of the attribute {@link #LOG_ID_ATTRIBUTE}
|
||||
* along with some extra formatting so that the prefix can be conveniently
|
||||
* prepended with no further formatting no separatorns required.
|
||||
* @return the log message prefix or an empty String if the
|
||||
* {@link #LOG_ID_ATTRIBUTE} is not set.
|
||||
* @since 5.1
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -31,6 +30,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -141,7 +141,7 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
||||
.filter(reader -> reader.canRead(FORM_DATA_TYPE, MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No form data HttpMessageReader.")))
|
||||
.readMono(FORM_DATA_TYPE, request, Collections.emptyMap())
|
||||
.readMono(FORM_DATA_TYPE, request, Hints.none())
|
||||
.switchIfEmpty(EMPTY_FORM_DATA)
|
||||
.cache();
|
||||
}
|
||||
@@ -163,7 +163,7 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
||||
.filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No multipart HttpMessageReader.")))
|
||||
.readMono(MULTIPART_DATA_TYPE, request, Collections.emptyMap())
|
||||
.readMono(MULTIPART_DATA_TYPE, request, Hints.none())
|
||||
.switchIfEmpty(EMPTY_MULTIPART_DATA)
|
||||
.cache();
|
||||
}
|
||||
|
||||
@@ -458,8 +458,7 @@ public abstract class BodyInserters {
|
||||
findWriter(context, MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA);
|
||||
MultiValueMap<String, HttpEntity<?>> body = this.builder.build();
|
||||
return messageWriter.write(Mono.just(body), MULTIPART_DATA_TYPE,
|
||||
MediaType.MULTIPART_FORM_DATA,
|
||||
outputMessage, context.hints());
|
||||
MediaType.MULTIPART_FORM_DATA, outputMessage, context.hints());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -251,7 +252,7 @@ final class DefaultClientRequestBuilder implements ClientRequest.Builder {
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -95,7 +96,7 @@ class DefaultClientResponse implements ClientResponse {
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
return Collections.emptyMap();
|
||||
return Hints.none();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -236,6 +237,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
hints.put(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix());
|
||||
return hints;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRange;
|
||||
@@ -123,11 +124,16 @@ class DefaultServerRequest implements ServerRequest {
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
|
||||
return body(extractor, Collections.emptyMap());
|
||||
return bodyInternal(extractor, Hints.from(Hints.LOG_PREFIX_HINT, exchange().getLogPrefix()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor, Map<String, Object> hints) {
|
||||
hints = Hints.merge(hints, Hints.LOG_PREFIX_HINT, exchange().getLogPrefix());
|
||||
return bodyInternal(extractor, hints);
|
||||
}
|
||||
|
||||
private <T> T bodyInternal(BodyExtractor<T, ? super ServerHttpRequest> extractor, Map<String, Object> hints) {
|
||||
return extractor.extract(request(),
|
||||
new BodyExtractor.Context() {
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -35,6 +34,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
@@ -320,7 +320,7 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
|
||||
.filter(reader -> reader.canRead(FORM_DATA_TYPE, MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No form data HttpMessageReader.")))
|
||||
.readMono(FORM_DATA_TYPE, request, Collections.emptyMap())
|
||||
.readMono(FORM_DATA_TYPE, request, Hints.none())
|
||||
.switchIfEmpty(EMPTY_FORM_DATA)
|
||||
.cache();
|
||||
}
|
||||
@@ -342,7 +342,7 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
|
||||
.filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No multipart HttpMessageReader.")))
|
||||
.readMono(MULTIPART_DATA_TYPE, request, Collections.emptyMap())
|
||||
.readMono(MULTIPART_DATA_TYPE, request, Hints.none())
|
||||
.switchIfEmpty(EMPTY_MULTIPART_DATA)
|
||||
.cache();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -399,6 +400,7 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
hints.put(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix());
|
||||
return hints;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,11 +23,8 @@ import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -36,7 +33,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.CacheControl;
|
||||
@@ -372,7 +369,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||
Assert.state(writer != null, "No ResourceHttpMessageWriter");
|
||||
return writer.write(Mono.just(resource),
|
||||
null, ResolvableType.forClass(Resource.class), mediaType,
|
||||
exchange.getRequest(), exchange.getResponse(), Collections.emptyMap());
|
||||
exchange.getRequest(), exchange.getResponse(),
|
||||
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return Mono.error(ex);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,7 +33,7 @@ import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -161,7 +160,7 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
|
||||
|
||||
for (HttpMessageReader<?> reader : getMessageReaders()) {
|
||||
if (reader.canRead(elementType, mediaType)) {
|
||||
Map<String, Object> readHints = Collections.emptyMap();
|
||||
Map<String, Object> readHints = Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix());
|
||||
if (adapter != null && adapter.isMultiValue()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(exchange.getLogPrefix() + "0..N [" + elementType + "]");
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -28,7 +26,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -149,8 +147,8 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
}
|
||||
for (HttpMessageWriter<?> writer : getMessageWriters()) {
|
||||
if (writer.canWrite(elementType, bestMediaType)) {
|
||||
return writer.write((Publisher) publisher, actualType, elementType,
|
||||
bestMediaType, request, response, Collections.emptyMap());
|
||||
return writer.write((Publisher) publisher, actualType, elementType, bestMediaType,
|
||||
request, response, Hints.from(Hints.LOG_PREFIX_HINT, logPrefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.web.reactive.result.view;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,6 +27,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
@@ -161,7 +161,8 @@ public class HttpMessageWriterView implements View {
|
||||
Publisher<T> input = Mono.justOrEmpty(value);
|
||||
ResolvableType elementType = ResolvableType.forClass(value.getClass());
|
||||
return ((HttpMessageWriter<T>) this.writer).write(
|
||||
input, elementType, contentType, exchange.getResponse(), Collections.emptyMap());
|
||||
input, elementType, contentType, exchange.getResponse(),
|
||||
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user