Skip to content

Commit 4c9ae64

Browse files
committed
Concurrency/nullability fine-tuning in MimeType(Utils)
See gh-22340
1 parent 1bf3b36 commit 4c9ae64

File tree

2 files changed

+26
-21
lines changed

2 files changed

+26
-21
lines changed

spring-core/src/main/java/org/springframework/util/MimeType.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ public class MimeType implements Comparable<MimeType>, Serializable {
103103

104104
private final Map<String, String> parameters;
105105

106-
private String mimetype;
106+
@Nullable
107+
private volatile String toStringValue;
107108

108109

109110
/**
@@ -471,12 +472,14 @@ public int hashCode() {
471472

472473
@Override
473474
public String toString() {
474-
if (this.mimetype == null) {
475+
String value = this.toStringValue;
476+
if (value == null) {
475477
StringBuilder builder = new StringBuilder();
476478
appendTo(builder);
477-
this.mimetype = builder.toString();
479+
value = builder.toString();
480+
this.toStringValue = value;
478481
}
479-
return this.mimetype;
482+
return value;
480483
}
481484

482485
protected void appendTo(StringBuilder builder) {

spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.util.stream.Collectors;
3737

3838
import org.springframework.lang.Nullable;
39-
import org.springframework.util.MimeType.SpecificityComparator;
4039

4140
/**
4241
* Miscellaneous {@link MimeType} utility methods.
@@ -55,13 +54,10 @@ public abstract class MimeTypeUtils {
5554
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
5655
'V', 'W', 'X', 'Y', 'Z'};
5756

58-
private static final ConcurrentLRUCache<String, MimeType> CACHED_MIMETYPES =
59-
new ConcurrentLRUCache<>(32, MimeTypeUtils::parseMimeTypeInternal);
60-
6157
/**
6258
* Comparator used by {@link #sortBySpecificity(List)}.
6359
*/
64-
public static final Comparator<MimeType> SPECIFICITY_COMPARATOR = new SpecificityComparator<>();
60+
public static final Comparator<MimeType> SPECIFICITY_COMPARATOR = new MimeType.SpecificityComparator<>();
6561

6662
/**
6763
* Public constant mime type that includes all media ranges (i.e. "&#42;/&#42;").
@@ -163,6 +159,10 @@ public abstract class MimeTypeUtils {
163159
*/
164160
public static final String TEXT_XML_VALUE = "text/xml";
165161

162+
163+
private static final ConcurrentLruCache<String, MimeType> cachedMimeTypes =
164+
new ConcurrentLruCache<>(32, MimeTypeUtils::parseMimeTypeInternal);
165+
166166
@Nullable
167167
private static volatile Random random;
168168

@@ -179,6 +179,7 @@ public abstract class MimeTypeUtils {
179179
TEXT_XML = new MimeType("text", "xml");
180180
}
181181

182+
182183
/**
183184
* Parse the given String into a single {@code MimeType}.
184185
* Recently parsed {@code MimeType} are cached for further retrieval.
@@ -187,7 +188,7 @@ public abstract class MimeTypeUtils {
187188
* @throws InvalidMimeTypeException if the string cannot be parsed
188189
*/
189190
public static MimeType parseMimeType(String mimeType) {
190-
return CACHED_MIMETYPES.get(mimeType);
191+
return cachedMimeTypes.get(mimeType);
191192
}
192193

193194
private static MimeType parseMimeTypeInternal(String mimeType) {
@@ -275,6 +276,7 @@ public static List<MimeType> parseMimeTypes(String mimeTypes) {
275276
.map(MimeTypeUtils::parseMimeType).collect(Collectors.toList());
276277
}
277278

279+
278280
/**
279281
* Tokenize the given comma-separated string of {@code MimeType} objects
280282
* into a {@code List<String>}. Unlike simple tokenization by ",", this
@@ -330,7 +332,6 @@ public static String toString(Collection<? extends MimeType> mimeTypes) {
330332
return builder.toString();
331333
}
332334

333-
334335
/**
335336
* Sorts the given list of {@code MimeType} objects by specificity.
336337
* <p>Given two mime types:
@@ -399,17 +400,17 @@ public static String generateMultipartBoundaryString() {
399400
return new String(generateMultipartBoundary(), StandardCharsets.US_ASCII);
400401
}
401402

403+
402404
/**
403405
* Simple Least Recently Used cache, bounded by the maximum size given
404406
* to the class constructor.
405-
* This implementation is backed by a {@code ConcurrentHashMap} for storing
406-
* the cached values and a {@code ConcurrentLinkedQueue} for ordering
407-
* the keys and choosing the least recently used key when the cache is at
408-
* full capacity.
409-
* @param <K> the type of the key used for caching
407+
* <p>This implementation is backed by a {@code ConcurrentHashMap} for storing
408+
* the cached values and a {@code ConcurrentLinkedQueue} for ordering the keys
409+
* and choosing the least recently used key when the cache is at full capacity.
410+
* @param <K> the type of the key used for caching
410411
* @param <V> the type of the cached values
411412
*/
412-
static class ConcurrentLRUCache<K, V> {
413+
private static class ConcurrentLruCache<K, V> {
413414

414415
private final int maxSize;
415416

@@ -421,14 +422,14 @@ static class ConcurrentLRUCache<K, V> {
421422

422423
private final Function<K, V> generator;
423424

424-
ConcurrentLRUCache(int maxSize, Function<K, V> generator) {
425+
public ConcurrentLruCache(int maxSize, Function<K, V> generator) {
425426
Assert.isTrue(maxSize > 0, "LRU max size should be positive");
426427
Assert.notNull(generator, "Generator function should not be null");
427428
this.maxSize = maxSize;
428429
this.generator = generator;
429430
}
430431

431-
V get(K key) {
432+
public V get(K key) {
432433
this.lock.readLock().lock();
433434
try {
434435
if (this.queue.remove(key)) {
@@ -443,7 +444,9 @@ V get(K key) {
443444
try {
444445
if (this.queue.size() == this.maxSize) {
445446
K leastUsed = this.queue.poll();
446-
this.cache.remove(leastUsed);
447+
if (leastUsed != null) {
448+
this.cache.remove(leastUsed);
449+
}
447450
}
448451
V value = this.generator.apply(key);
449452
this.queue.add(key);
@@ -454,7 +457,6 @@ V get(K key) {
454457
this.lock.writeLock().unlock();
455458
}
456459
}
457-
458460
}
459461

460462
}

0 commit comments

Comments
 (0)