Declare resolvedCharset as transient (restoring serializability)

Closes gh-26127
This commit is contained in:
Juergen Hoeller 2020-11-20 18:40:10 +01:00
parent e6324bd578
commit 4fb5d59c64
4 changed files with 50 additions and 18 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.util; package org.springframework.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.BitSet; import java.util.BitSet;
@ -104,7 +106,7 @@ public class MimeType implements Comparable<MimeType>, Serializable {
private final Map<String, String> parameters; private final Map<String, String> parameters;
@Nullable @Nullable
private Charset resolvedCharset; private transient Charset resolvedCharset;
@Nullable @Nullable
private volatile String toStringValue; private volatile String toStringValue;
@ -184,9 +186,9 @@ public class MimeType implements Comparable<MimeType>, Serializable {
this.subtype = subtype.toLowerCase(Locale.ENGLISH); this.subtype = subtype.toLowerCase(Locale.ENGLISH);
if (!CollectionUtils.isEmpty(parameters)) { if (!CollectionUtils.isEmpty(parameters)) {
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH); Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH);
parameters.forEach((attribute, value) -> { parameters.forEach((parameter, value) -> {
checkParameters(attribute, value); checkParameters(parameter, value);
map.put(attribute, value); map.put(parameter, value);
}); });
this.parameters = Collections.unmodifiableMap(map); this.parameters = Collections.unmodifiableMap(map);
} }
@ -224,11 +226,11 @@ public class MimeType implements Comparable<MimeType>, Serializable {
} }
} }
protected void checkParameters(String attribute, String value) { protected void checkParameters(String parameter, String value) {
Assert.hasLength(attribute, "'attribute' must not be empty"); Assert.hasLength(parameter, "'parameter' must not be empty");
Assert.hasLength(value, "'value' must not be empty"); Assert.hasLength(value, "'value' must not be empty");
checkToken(attribute); checkToken(parameter);
if (PARAM_CHARSET.equals(attribute)) { if (PARAM_CHARSET.equals(parameter)) {
if (this.resolvedCharset == null) { if (this.resolvedCharset == null) {
this.resolvedCharset = Charset.forName(unquote(value)); this.resolvedCharset = Charset.forName(unquote(value));
} }
@ -591,6 +593,17 @@ public class MimeType implements Comparable<MimeType>, Serializable {
return 0; 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, * Parse the given String value into a {@code MimeType} object,

View File

@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -267,13 +268,13 @@ class MimeTypeTests {
assertThat(mimeType.getParameter("attr")).isEqualTo("'v>alue'"); assertThat(mimeType.getParameter("attr")).isEqualTo("'v>alue'");
} }
@Test // SPR-16630 @Test // SPR-16630
void parseMimeTypeWithSpacesAroundEquals() { void parseMimeTypeWithSpacesAroundEquals() {
MimeType mimeType = MimeTypeUtils.parseMimeType("multipart/x-mixed-replace;boundary = --myboundary"); MimeType mimeType = MimeTypeUtils.parseMimeType("multipart/x-mixed-replace;boundary = --myboundary");
assertThat(mimeType.getParameter("boundary")).isEqualTo("--myboundary"); assertThat(mimeType.getParameter("boundary")).isEqualTo("--myboundary");
} }
@Test // SPR-16630 @Test // SPR-16630
void parseMimeTypeWithSpacesAroundEqualsAndQuotedValue() { void parseMimeTypeWithSpacesAroundEqualsAndQuotedValue() {
MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain; foo = \" bar \" "); MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain; foo = \" bar \" ");
assertThat(mimeType.getParameter("foo")).isEqualTo("\" bar \""); assertThat(mimeType.getParameter("foo")).isEqualTo("\" bar \"");
@ -303,14 +304,14 @@ class MimeTypeTests {
assertThat(mimeTypes.size()).as("Invalid amount of mime types").isEqualTo(0); assertThat(mimeTypes.size()).as("Invalid amount of mime types").isEqualTo(0);
} }
@Test // gh-23241 @Test // gh-23241
void parseMimeTypesWithTrailingComma() { void parseMimeTypesWithTrailingComma() {
List<MimeType> mimeTypes = MimeTypeUtils.parseMimeTypes("text/plain, text/html,"); List<MimeType> mimeTypes = MimeTypeUtils.parseMimeTypes("text/plain, text/html,");
assertThat(mimeTypes).as("No mime types returned").isNotNull(); assertThat(mimeTypes).as("No mime types returned").isNotNull();
assertThat(mimeTypes.size()).as("Incorrect number of mime types").isEqualTo(2); assertThat(mimeTypes.size()).as("Incorrect number of mime types").isEqualTo(2);
} }
@Test // SPR-17459 @Test // SPR-17459
void parseMimeTypesWithQuotedParameters() { void parseMimeTypesWithQuotedParameters() {
testWithQuotedParameters("foo/bar;param=\",\""); testWithQuotedParameters("foo/bar;param=\",\"");
testWithQuotedParameters("foo/bar;param=\"s,a,\""); testWithQuotedParameters("foo/bar;param=\"s,a,\"");
@ -332,7 +333,7 @@ class MimeTypeTests {
assertThat(type.getSubtypeSuffix()).isEqualTo("json"); assertThat(type.getSubtypeSuffix()).isEqualTo("json");
} }
@Test // gh-25350 @Test // gh-25350
void wildcardSubtypeCompatibleWithSuffix() { void wildcardSubtypeCompatibleWithSuffix() {
MimeType applicationStar = new MimeType("application", "*"); MimeType applicationStar = new MimeType("application", "*");
MimeType applicationVndJson = new MimeType("application", "vnd.something+json"); MimeType applicationVndJson = new MimeType("application", "vnd.something+json");
@ -413,4 +414,12 @@ class MimeTypeTests {
assertThat(m2.compareTo(m1)).isEqualTo(0); 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);
}
} }

View File

@ -514,9 +514,9 @@ public class MediaType extends MimeType implements Serializable {
@Override @Override
protected void checkParameters(String attribute, String value) { protected void checkParameters(String parameter, String value) {
super.checkParameters(attribute, value); super.checkParameters(parameter, value);
if (PARAM_QUALITY_FACTOR.equals(attribute)) { if (PARAM_QUALITY_FACTOR.equals(parameter)) {
value = unquote(value); value = unquote(value);
double d = Double.parseDouble(value); double d = Double.parseDouble(value);
Assert.isTrue(d >= 0D && d <= 1D, Assert.isTrue(d >= 0D && d <= 1D,

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.http; package org.springframework.http;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; 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.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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); assertThat(mediaTypes.size()).as("Invalid amount of media types").isEqualTo(0);
} }
@Test // gh-23241 @Test // gh-23241
public void parseMediaTypesWithTrailingComma() { public void parseMediaTypesWithTrailingComma() {
List<MediaType> mediaTypes = MediaType.parseMediaTypes("text/plain, text/html, "); List<MediaType> mediaTypes = MediaType.parseMediaTypes("text/plain, text/html, ");
assertThat(mediaTypes).as("No media types returned").isNotNull(); assertThat(mediaTypes).as("No media types returned").isNotNull();
@ -460,4 +462,12 @@ public class MediaTypeTests {
assertThat(new MediaType("text", "*").isConcrete()).as("text/* concrete").isFalse(); 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);
}
} }