Skip to content

Commit 1956cb1

Browse files
committed
Defensive concurrent access to shared file extension data structures
Closes gh-23064
1 parent 4fc9747 commit 1956cb1

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,11 +23,10 @@
2323
import java.util.Map;
2424
import java.util.concurrent.ConcurrentHashMap;
2525
import java.util.concurrent.ConcurrentMap;
26+
import java.util.concurrent.CopyOnWriteArrayList;
2627

2728
import org.springframework.http.MediaType;
2829
import org.springframework.lang.Nullable;
29-
import org.springframework.util.LinkedMultiValueMap;
30-
import org.springframework.util.MultiValueMap;
3130

3231
/**
3332
* An implementation of {@code MediaTypeFileExtensionResolver} that maintains
@@ -37,28 +36,31 @@
3736
* Subsequently subclasses can use {@link #addMapping} to add more mappings.
3837
*
3938
* @author Rossen Stoyanchev
39+
* @author Juergen Hoeller
4040
* @since 3.2
4141
*/
4242
public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExtensionResolver {
4343

4444
private final ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<>(64);
4545

46-
private final MultiValueMap<MediaType, String> fileExtensions = new LinkedMultiValueMap<>();
46+
private final ConcurrentMap<MediaType, List<String>> fileExtensions = new ConcurrentHashMap<>(64);
4747

48-
private final List<String> allFileExtensions = new ArrayList<>();
48+
private final List<String> allFileExtensions = new CopyOnWriteArrayList<>();
4949

5050

5151
/**
5252
* Create an instance with the given map of file extensions and media types.
5353
*/
5454
public MappingMediaTypeFileExtensionResolver(@Nullable Map<String, MediaType> mediaTypes) {
5555
if (mediaTypes != null) {
56+
List<String> allFileExtensions = new ArrayList<>();
5657
mediaTypes.forEach((extension, mediaType) -> {
5758
String lowerCaseExtension = extension.toLowerCase(Locale.ENGLISH);
5859
this.mediaTypes.put(lowerCaseExtension, mediaType);
59-
this.fileExtensions.add(mediaType, lowerCaseExtension);
60-
this.allFileExtensions.add(lowerCaseExtension);
60+
addFileExtension(mediaType, extension);
61+
allFileExtensions.add(lowerCaseExtension);
6162
});
63+
this.allFileExtensions.addAll(allFileExtensions);
6264
}
6365
}
6466

@@ -77,11 +79,17 @@ protected List<MediaType> getAllMediaTypes() {
7779
protected void addMapping(String extension, MediaType mediaType) {
7880
MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
7981
if (previous == null) {
80-
this.fileExtensions.add(mediaType, extension);
82+
addFileExtension(mediaType, extension);
8183
this.allFileExtensions.add(extension);
8284
}
8385
}
8486

87+
private void addFileExtension(MediaType mediaType, String extension) {
88+
List<String> newList = new CopyOnWriteArrayList<>();
89+
List<String> oldList = this.fileExtensions.putIfAbsent(mediaType, newList);
90+
(oldList != null ? oldList : newList).add(extension);
91+
}
92+
8593

8694
@Override
8795
public List<String> resolveFileExtensions(MediaType mediaType) {

0 commit comments

Comments
 (0)