From 4fb5d59c644a03c094bd4ecba562eab750815722 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 20 Nov 2020 18:40:10 +0100 Subject: [PATCH] Declare resolvedCharset as transient (restoring serializability) Closes gh-26127 --- .../org/springframework/util/MimeType.java | 29 ++++++++++++++----- .../springframework/util/MimeTypeTests.java | 19 ++++++++---- .../org/springframework/http/MediaType.java | 6 ++-- .../springframework/http/MediaTypeTests.java | 14 +++++++-- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index e431589085..de29040b07 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -16,6 +16,8 @@ package org.springframework.util; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.util.BitSet; @@ -104,7 +106,7 @@ public class MimeType implements Comparable, Serializable { private final Map parameters; @Nullable - private Charset resolvedCharset; + private transient Charset resolvedCharset; @Nullable private volatile String toStringValue; @@ -184,9 +186,9 @@ public class MimeType implements Comparable, Serializable { this.subtype = subtype.toLowerCase(Locale.ENGLISH); if (!CollectionUtils.isEmpty(parameters)) { Map map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH); - parameters.forEach((attribute, value) -> { - checkParameters(attribute, value); - map.put(attribute, value); + parameters.forEach((parameter, value) -> { + checkParameters(parameter, value); + map.put(parameter, value); }); this.parameters = Collections.unmodifiableMap(map); } @@ -224,11 +226,11 @@ public class MimeType implements Comparable, Serializable { } } - protected void checkParameters(String attribute, String value) { - Assert.hasLength(attribute, "'attribute' must not be empty"); + protected void checkParameters(String parameter, String value) { + Assert.hasLength(parameter, "'parameter' must not be empty"); Assert.hasLength(value, "'value' must not be empty"); - checkToken(attribute); - if (PARAM_CHARSET.equals(attribute)) { + checkToken(parameter); + if (PARAM_CHARSET.equals(parameter)) { if (this.resolvedCharset == null) { this.resolvedCharset = Charset.forName(unquote(value)); } @@ -591,6 +593,17 @@ public class MimeType implements Comparable, Serializable { return 0; } + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Rely on default serialization, just initialize state after deserialization. + ois.defaultReadObject(); + + // Initialize transient fields. + String charsetName = getParameter(PARAM_CHARSET); + if (charsetName != null) { + this.resolvedCharset = Charset.forName(unquote(charsetName)); + } + } + /** * Parse the given String value into a {@code MimeType} object, diff --git a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java index 7eb86378a5..38c7c8a5bf 100644 --- a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java +++ b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; @@ -267,13 +268,13 @@ class MimeTypeTests { assertThat(mimeType.getParameter("attr")).isEqualTo("'v>alue'"); } - @Test // SPR-16630 + @Test // SPR-16630 void parseMimeTypeWithSpacesAroundEquals() { MimeType mimeType = MimeTypeUtils.parseMimeType("multipart/x-mixed-replace;boundary = --myboundary"); assertThat(mimeType.getParameter("boundary")).isEqualTo("--myboundary"); } - @Test // SPR-16630 + @Test // SPR-16630 void parseMimeTypeWithSpacesAroundEqualsAndQuotedValue() { MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain; foo = \" bar \" "); assertThat(mimeType.getParameter("foo")).isEqualTo("\" bar \""); @@ -303,14 +304,14 @@ class MimeTypeTests { assertThat(mimeTypes.size()).as("Invalid amount of mime types").isEqualTo(0); } - @Test // gh-23241 + @Test // gh-23241 void parseMimeTypesWithTrailingComma() { List mimeTypes = MimeTypeUtils.parseMimeTypes("text/plain, text/html,"); assertThat(mimeTypes).as("No mime types returned").isNotNull(); assertThat(mimeTypes.size()).as("Incorrect number of mime types").isEqualTo(2); } - @Test // SPR-17459 + @Test // SPR-17459 void parseMimeTypesWithQuotedParameters() { testWithQuotedParameters("foo/bar;param=\",\""); testWithQuotedParameters("foo/bar;param=\"s,a,\""); @@ -332,7 +333,7 @@ class MimeTypeTests { assertThat(type.getSubtypeSuffix()).isEqualTo("json"); } - @Test // gh-25350 + @Test // gh-25350 void wildcardSubtypeCompatibleWithSuffix() { MimeType applicationStar = new MimeType("application", "*"); MimeType applicationVndJson = new MimeType("application", "vnd.something+json"); @@ -413,4 +414,12 @@ class MimeTypeTests { assertThat(m2.compareTo(m1)).isEqualTo(0); } + @Test // gh-26127 + void serialize() throws Exception { + MimeType original = new MimeType("text", "plain", StandardCharsets.UTF_8); + MimeType deserialized = SerializationTestUtils.serializeAndDeserialize(original); + assertThat(deserialized).isEqualTo(original); + assertThat(original).isEqualTo(deserialized); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index d338e356c7..f4af4c01a5 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -514,9 +514,9 @@ public class MediaType extends MimeType implements Serializable { @Override - protected void checkParameters(String attribute, String value) { - super.checkParameters(attribute, value); - if (PARAM_QUALITY_FACTOR.equals(attribute)) { + protected void checkParameters(String parameter, String value) { + super.checkParameters(parameter, value); + if (PARAM_QUALITY_FACTOR.equals(parameter)) { value = unquote(value); double d = Double.parseDouble(value); Assert.isTrue(d >= 0D && d <= 1D, diff --git a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java index 8849ff7ba4..74e73bfa71 100644 --- a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java +++ b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -16,6 +16,7 @@ package org.springframework.http; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -26,6 +27,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -160,7 +162,7 @@ public class MediaTypeTests { assertThat(mediaTypes.size()).as("Invalid amount of media types").isEqualTo(0); } - @Test // gh-23241 + @Test // gh-23241 public void parseMediaTypesWithTrailingComma() { List mediaTypes = MediaType.parseMediaTypes("text/plain, text/html, "); assertThat(mediaTypes).as("No media types returned").isNotNull(); @@ -460,4 +462,12 @@ public class MediaTypeTests { assertThat(new MediaType("text", "*").isConcrete()).as("text/* concrete").isFalse(); } + @Test // gh-26127 + void serialize() throws Exception { + MediaType original = new MediaType("text", "plain", StandardCharsets.UTF_8); + MediaType deserialized = SerializationTestUtils.serializeAndDeserialize(original); + assertThat(deserialized).isEqualTo(original); + assertThat(original).isEqualTo(deserialized); + } + }