diff --git a/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java index 27f5899b9a..40feff2b17 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/AbstractEncoder.java @@ -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 implements Encoder { 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 hints) { - return hints != null ? ((Log) hints.getOrDefault(Log.class.getName(), logger)) : logger; - } - } diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java index ce013679d7..c5c41256bb 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayDecoder.java @@ -54,7 +54,7 @@ public class ByteArrayDecoder extends AbstractDataBufferDecoder { 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; } diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayEncoder.java index 33672fb399..433b71ae02 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteArrayEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteArrayEncoder.java @@ -55,9 +55,10 @@ public class ByteArrayEncoder extends AbstractEncoder { 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; }); diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java index 50735d8c7b..ec79280e5e 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferDecoder.java @@ -58,7 +58,7 @@ public class ByteBufferDecoder extends AbstractDataBufferDecoder { copy.flip(); DataBufferUtils.release(dataBuffer); if (logger.isDebugEnabled()) { - logger.debug("Read " + byteCount + " bytes"); + logger.debug(Hints.getLogPrefix(hints) + "Read " + byteCount + " bytes"); } return copy; } diff --git a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferEncoder.java index 13620310df..d5b02b7e82 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ByteBufferEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ByteBufferEncoder.java @@ -56,9 +56,10 @@ public class ByteBufferEncoder extends AbstractEncoder { 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; }); diff --git a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java index 9b067ca1e1..e59879b699 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java @@ -69,9 +69,9 @@ public final class CharSequenceEncoder extends AbstractEncoder { 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); diff --git a/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java index bd070b91c9..a690dff3d4 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/DataBufferDecoder.java @@ -63,7 +63,7 @@ public class DataBufferDecoder extends AbstractDataBufferDecoder { @Nullable MimeType mimeType, @Nullable Map hints) { if (logger.isDebugEnabled()) { - logger.debug("Read " + buffer.readableByteCount() + " bytes"); + logger.debug(Hints.getLogPrefix(hints) + "Read " + buffer.readableByteCount() + " bytes"); } return buffer; } diff --git a/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java index a5cbe53696..2ff10ce6cb 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/DataBufferEncoder.java @@ -55,9 +55,11 @@ public class DataBufferEncoder extends AbstractEncoder { Flux 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; diff --git a/spring-core/src/main/java/org/springframework/core/codec/Hints.java b/spring-core/src/main/java/org/springframework/core/codec/Hints.java new file mode 100644 index 0000000000..87e3d4586c --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/codec/Hints.java @@ -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 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 none() { + return Collections.emptyMap(); + } + + /** + * Obtain the value for a required hint. + * @param hints the hints map + * @param hintName the required hint name + * @param the hint type to cast to + * @return the hint value + * @throws IllegalArgumentException if the hint is not found + */ + @SuppressWarnings("unchecked") + public static T getRequiredHint(@Nullable Map 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 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 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 merge(Map hints1, Map hints2) { + if (hints1.isEmpty() && hints2.isEmpty()) { + return Collections.emptyMap(); + } + else if (hints2.isEmpty()) { + return hints1; + } + else if (hints1.isEmpty()) { + return hints2; + } + else { + Map 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 merge(Map hints, String hintName, Object hintValue) { + if (hints.isEmpty()) { + return Collections.singletonMap(hintName, hintValue); + } + else { + Map result = new HashMap<>(hints.size() + 1); + result.putAll(hints); + result.put(hintName, hintValue); + return result; + } + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java index 923a21697c..f3dade6f3c 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ResourceDecoder.java @@ -73,7 +73,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder { 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) { diff --git a/spring-core/src/main/java/org/springframework/core/codec/ResourceEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/ResourceEncoder.java index 5bb33f2daa..41c2c90aca 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ResourceEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ResourceEncoder.java @@ -69,9 +69,9 @@ public class ResourceEncoder extends AbstractSingleValueEncoder { protected Flux encode(Resource resource, DataBufferFactory dataBufferFactory, ResolvableType type, @Nullable MimeType mimeType, @Nullable Map 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); diff --git a/spring-core/src/main/java/org/springframework/core/codec/ResourceRegionEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/ResourceRegionEncoder.java index aa06fb904f..96a9672ecf 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/ResourceRegionEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/ResourceRegionEncoder.java @@ -91,10 +91,7 @@ public class ResourceRegionEncoder extends AbstractEncoder { .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 { 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 in = DataBufferUtils.read(resource, position, bufferFactory, this.bufferSize); @@ -149,7 +147,8 @@ public class ResourceRegionEncoder extends AbstractEncoder { 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"); diff --git a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java index 40e5d9581b..c16d58f96d 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java @@ -207,7 +207,7 @@ public final class StringDecoder extends AbstractDataBufferDecoder { DataBufferUtils.release(dataBuffer); String value = charBuffer.toString(); if (logger.isDebugEnabled()) { - logger.debug("Decoded '" + value + "'"); + logger.debug(Hints.getLogPrefix(hints) + "Decoded '" + value + "'"); } return value; } diff --git a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java index 3291f97feb..12bbb746b1 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java @@ -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 implements HttpMessageReader { public Flux read(ResolvableType actualType, ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response, Map hints) { - Map allHints = new HashMap<>(4); - allHints.putAll(getReadHints(actualType, elementType, request, response)); - allHints.putAll(hints); + Map allHints = Hints.merge(hints, + getReadHints(actualType, elementType, request, response)); return read(elementType, request, allHints); } @@ -125,9 +123,8 @@ public class DecoderHttpMessageReader implements HttpMessageReader { public Mono readMono(ResolvableType actualType, ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response, Map hints) { - Map allHints = new HashMap<>(4); - allHints.putAll(getReadHints(actualType, elementType, request, response)); - allHints.putAll(hints); + Map allHints = Hints.merge(hints, + getReadHints(actualType, elementType, request, response)); return readMono(elementType, request, allHints); } @@ -141,10 +138,10 @@ public class DecoderHttpMessageReader implements HttpMessageReader { 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(); } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java index ad52e6ce18..7c4860fcff 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java @@ -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 implements HttpMessageWriter { ResolvableType elementType, @Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response, Map hints) { - Map allHints = new HashMap<>(); - allHints.putAll(getWriteHints(actualType, elementType, mediaType, request, response)); - allHints.putAll(hints); + Map allHints = Hints.merge(hints, + getWriteHints(actualType, elementType, mediaType, request, response)); return write(inputStream, elementType, mediaType, response, allHints); } @@ -175,10 +173,10 @@ public class EncoderHttpMessageWriter implements HttpMessageWriter { @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(); } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java index 3abf6525b0..477ea85bd3 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java @@ -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 formData = parseFormData(charset, body); if (shouldLogRequestDetails()) { - logger.debug("Decoded " + formData); + logger.debug(Hints.getLogPrefix(hints) + "Decoded " + formData); } return formData; }); diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java index bfb6e262b4..b64acf9fa2 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java @@ -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); diff --git a/spring-web/src/main/java/org/springframework/http/codec/HttpMessageEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/HttpMessageEncoder.java index 7488465080..4bc6a71edf 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/HttpMessageEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/HttpMessageEncoder.java @@ -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 extends Encoder { default Map getEncodeHints(ResolvableType actualType, ResolvableType elementType, @Nullable MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) { - return Collections.emptyMap(); + return Hints.none(); } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java index 908cf4f4eb..0b51c153a6 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ResourceHttpMessageWriter.java @@ -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 { ReactiveHttpOutputMessage message, Map 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 { } } - return zeroCopy(resource, null, message) + return zeroCopy(resource, null, message, hints) .orElseGet(() -> { Mono input = Mono.just(resource); DataBufferFactory factory = message.bufferFactory(); @@ -138,13 +137,15 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter { }); } - private static MediaType getResourceMediaType(@Nullable MediaType mediaType, Resource resource) { + private static MediaType getResourceMediaType( + @Nullable MediaType mediaType, Resource resource, Map 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 { } private static Optional> zeroCopy(Resource resource, @Nullable ResourceRegion region, - ReactiveHttpOutputMessage message) { + ReactiveHttpOutputMessage message, Map hints) { if (message instanceof ZeroCopyHttpOutputMessage && resource.isFile()) { try { @@ -171,7 +172,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter { 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 { response.setStatusCode(HttpStatus.PARTIAL_CONTENT); List 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 { 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 theHints = new HashMap<>(hints); - theHints.put(ResourceRegionEncoder.BOUNDARY_STRING_HINT, boundary); - return encodeAndWriteRegions(Flux.fromIterable(regions), resourceMediaType, response, theHints); + Map allHints = Hints.merge(hints, ResourceRegionEncoder.BOUNDARY_STRING_HINT, boundary); + return encodeAndWriteRegions(Flux.fromIterable(regions), resourceMediaType, response, allHints); } }); } - private Mono writeSingleRegion(ResourceRegion region, ReactiveHttpOutputMessage message) { + private Mono writeSingleRegion(ResourceRegion region, ReactiveHttpOutputMessage message, + Map hints) { - return zeroCopy(region.getResource(), region, message) + return zeroCopy(region.getResource(), region, message, hints) .orElseGet(() -> { Publisher input = Mono.just(region); MediaType mediaType = message.getHeaders().getContentType(); - return encodeAndWriteRegions(input, mediaType, message, Collections.emptyMap()); + return encodeAndWriteRegions(input, mediaType, message, hints); }); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java index 00da032fe1..9972241ad5 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java @@ -105,7 +105,7 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader line.equals("")) .concatMap(lines -> buildEvent(lines, valueType, shouldWrap, hints)); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageWriter.java index 7645c9f097..61e272e4cf 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageWriter.java @@ -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 hints) { - Map allHints = new HashMap<>(); - allHints.putAll(getEncodeHints(actualType, elementType, mediaType, request, response)); - allHints.putAll(hints); + Map 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 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(); } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java index d4bc4a2ddc..06edaa4f12 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java @@ -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; } diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java index 14e6b7762d..b4f167a3a6 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java @@ -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 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 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 ... diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java index 7f7af24a4c..be3971399b 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java @@ -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 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 diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java index dbb3ce1386..24dc8c0bd4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java @@ -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 DEFAULT_HINTS = - Collections.singletonMap(Log.class.getName(), new NoOpLog()); + private static final Map DEFAULT_HINTS = Hints.from(Hints.LOGGER_HINT, new NoOpLog()); private final List> 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 writeMultipart(MultiValueMap map, ReactiveHttpOutputMessage outputMessage) { + private Mono writeMultipart( + MultiValueMap map, ReactiveHttpOutputMessage outputMessage, Map hints) { + byte[] boundary = generateMultipartBoundary(); Map 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 body = Flux.fromIterable(map.entrySet()) diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java index cb4aa41dc3..26cd7e32c9 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java @@ -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 + "]"); } }); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java index 0e1da36691..97bdcaffb8 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java @@ -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 { 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; }); diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java index 7b07a3893d..e5aec2d995 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java @@ -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 { protected Flux encode(Object value, DataBufferFactory dataBufferFactory, ResolvableType type, @Nullable MimeType mimeType, @Nullable Map 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(); diff --git a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java index 46dd658db3..045ce06e76 100644 --- a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java +++ b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java @@ -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 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 diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java index 5bc0068794..faa261bd6a 100644 --- a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java +++ b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java @@ -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(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java index 91b685bb4f..862857409b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java @@ -458,8 +458,7 @@ public abstract class BodyInserters { findWriter(context, MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA); MultiValueMap> 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()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java index 6dfbab0965..015c56a6d5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java @@ -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 hints() { - return Collections.emptyMap(); + return Hints.none(); } }); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java index 64647ccdb1..ea74a3835f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java @@ -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 hints() { - return Collections.emptyMap(); + return Hints.none(); } }); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java index ee09b5e22c..0da501e6aa 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java @@ -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 implements EntityResponse.Builder { } @Override public Map hints() { + hints.put(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()); return hints; } }); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java index 3fea5d8aa1..21e0623bff 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java @@ -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 body(BodyExtractor extractor) { - return body(extractor, Collections.emptyMap()); + return bodyInternal(extractor, Hints.from(Hints.LOG_PREFIX_HINT, exchange().getLogPrefix())); } @Override public T body(BodyExtractor extractor, Map hints) { + hints = Hints.merge(hints, Hints.LOG_PREFIX_HINT, exchange().getLogPrefix()); + return bodyInternal(extractor, hints); + } + + private T bodyInternal(BodyExtractor extractor, Map hints) { return extractor.extract(request(), new BodyExtractor.Context() { @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java index f850c052b5..aebd82fd77 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequestBuilder.java @@ -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(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index b7db95cf36..0b455ecaf8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -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 hints() { + hints.put(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()); return hints; } }); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index cc6681a46d..497e7dd9ed 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -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); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 9af6b53c96..916b56f04b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -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 readHints = Collections.emptyMap(); + Map readHints = Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()); if (adapter != null && adapter.isMultiValue()) { if (logger.isDebugEnabled()) { logger.debug(exchange.getLogPrefix() + "0..N [" + elementType + "]"); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java index 63cb056101..9e2c04af6d 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java @@ -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)); } } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java index 4b181e551d..4817615e03 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java @@ -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 input = Mono.justOrEmpty(value); ResolvableType elementType = ResolvableType.forClass(value.getClass()); return ((HttpMessageWriter) this.writer).write( - input, elementType, contentType, exchange.getResponse(), Collections.emptyMap()); + input, elementType, contentType, exchange.getResponse(), + Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix())); } }