Browse Source

Defensive concurrent access to shared file extension data structures

Closes gh-23064
pull/23837/head
Juergen Hoeller 6 years ago
parent
commit
1956cb1e57
  1. 24
      spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 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.
@ -23,11 +23,10 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/** /**
* An implementation of {@code MediaTypeFileExtensionResolver} that maintains * An implementation of {@code MediaTypeFileExtensionResolver} that maintains
@ -37,15 +36,16 @@ import org.springframework.util.MultiValueMap;
* Subsequently subclasses can use {@link #addMapping} to add more mappings. * Subsequently subclasses can use {@link #addMapping} to add more mappings.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.2 * @since 3.2
*/ */
public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExtensionResolver { public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExtensionResolver {
private final ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<>(64); private final ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<>(64);
private final MultiValueMap<MediaType, String> fileExtensions = new LinkedMultiValueMap<>(); private final ConcurrentMap<MediaType, List<String>> fileExtensions = new ConcurrentHashMap<>(64);
private final List<String> allFileExtensions = new ArrayList<>(); private final List<String> allFileExtensions = new CopyOnWriteArrayList<>();
/** /**
@ -53,12 +53,14 @@ public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExten
*/ */
public MappingMediaTypeFileExtensionResolver(@Nullable Map<String, MediaType> mediaTypes) { public MappingMediaTypeFileExtensionResolver(@Nullable Map<String, MediaType> mediaTypes) {
if (mediaTypes != null) { if (mediaTypes != null) {
List<String> allFileExtensions = new ArrayList<>();
mediaTypes.forEach((extension, mediaType) -> { mediaTypes.forEach((extension, mediaType) -> {
String lowerCaseExtension = extension.toLowerCase(Locale.ENGLISH); String lowerCaseExtension = extension.toLowerCase(Locale.ENGLISH);
this.mediaTypes.put(lowerCaseExtension, mediaType); this.mediaTypes.put(lowerCaseExtension, mediaType);
this.fileExtensions.add(mediaType, lowerCaseExtension); addFileExtension(mediaType, extension);
this.allFileExtensions.add(lowerCaseExtension); allFileExtensions.add(lowerCaseExtension);
}); });
this.allFileExtensions.addAll(allFileExtensions);
} }
} }
@ -77,11 +79,17 @@ public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExten
protected void addMapping(String extension, MediaType mediaType) { protected void addMapping(String extension, MediaType mediaType) {
MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType); MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
if (previous == null) { if (previous == null) {
this.fileExtensions.add(mediaType, extension); addFileExtension(mediaType, extension);
this.allFileExtensions.add(extension); this.allFileExtensions.add(extension);
} }
} }
private void addFileExtension(MediaType mediaType, String extension) {
List<String> newList = new CopyOnWriteArrayList<>();
List<String> oldList = this.fileExtensions.putIfAbsent(mediaType, newList);
(oldList != null ? oldList : newList).add(extension);
}
@Override @Override
public List<String> resolveFileExtensions(MediaType mediaType) { public List<String> resolveFileExtensions(MediaType mediaType) {

Loading…
Cancel
Save