diff --git a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java
index 1133b060f5..a299784a6a 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -26,19 +26,30 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * A base class for ContentNegotiationStrategy types that maintain a map with keys
- * such as "json" and media types such as "application/json".
+ * Base class for {@code ContentNegotiationStrategy} implementations with the
+ * steps to resolve a request to media types.
+ *
+ *
First a key (e.g. "json", "pdf") must be extracted from the request (e.g.
+ * file extension, query param). The key must then be resolved to media type(s)
+ * through the base class {@link MappingMediaTypeFileExtensionResolver} which
+ * stores such mappings.
+ *
+ *
The method {@link #handleNoMatch} allow sub-classes to plug in additional
+ * ways of looking up media types (e.g. through the Java Activation framework,
+ * or {@link javax.servlet.ServletContext#getMimeType}. Media types resolved
+ * via base classes are then added to the base class
+ * {@link MappingMediaTypeFileExtensionResolver}, i.e. cached for new lookups.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
-public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeFileExtensionResolver
- implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
+public abstract class AbstractMappingContentNegotiationStrategy
+ extends MappingMediaTypeFileExtensionResolver
+ implements ContentNegotiationStrategy {
/**
- * Create an instance with the given extension-to-MediaType lookup.
- * @throws IllegalArgumentException if a media type string cannot be parsed
+ * Create an instance with the given map of file extensions and media types.
*/
public AbstractMappingContentNegotiationStrategy(Map mediaTypes) {
super(mediaTypes);
@@ -46,7 +57,9 @@ public abstract class AbstractMappingContentNegotiationStrategy extends MappingM
@Override
- public List resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
+ public List resolveMediaTypes(NativeWebRequest webRequest)
+ throws HttpMediaTypeNotAcceptableException {
+
String key = getMediaTypeKey(webRequest);
if (StringUtils.hasText(key)) {
MediaType mediaType = lookupMediaType(key);
@@ -64,22 +77,27 @@ public abstract class AbstractMappingContentNegotiationStrategy extends MappingM
}
/**
- * Sub-classes must extract the key to use to look up a media type.
- * @return the lookup key or {@code null} if the key cannot be derived
+ * Extract a key from the request to use to look up media types.
+ * @return the lookup key or {@code null}.
*/
protected abstract String getMediaTypeKey(NativeWebRequest request);
/**
- * Invoked when a matching media type is found in the lookup map.
+ * Override to provide handling when a key is successfully resolved via
+ * {@link #lookupMediaType}.
*/
- protected void handleMatch(String mappingKey, MediaType mediaType) {
+ protected void handleMatch(String key, MediaType mediaType) {
}
/**
- * Invoked when no matching media type is found in the lookup map.
- * Sub-classes can take further steps to determine the media type.
+ * Override to provide handling when a key is not resolved via.
+ * {@link #lookupMediaType}. Sub-classes can take further steps to
+ * determine the media type(s). If a MediaType is returned from
+ * this method it will be added to the cache in the base class.
*/
- protected MediaType handleNoMatch(NativeWebRequest request, String key) throws HttpMediaTypeNotAcceptableException {
+ protected MediaType handleNoMatch(NativeWebRequest request, String key)
+ throws HttpMediaTypeNotAcceptableException {
+
return null;
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java
index 9120a52fae..babf45c68e 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -30,63 +30,52 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * This class is used to determine the requested {@linkplain MediaType media types}
- * of a request by delegating to a list of ContentNegotiationStrategy instances.
- * The strategies must be provided at instantiation or alternatively if using
- * the default constructor, an instance of {@link HeaderContentNegotiationStrategy}
- * will be configured by default.
+ * Central class to determine requested {@linkplain MediaType media types}
+ * for a request. This is done by delegating to a list of configured
+ * {@code ContentNegotiationStrategy} instances.
*
- * This class may also be used to look up file extensions associated with a
- * MediaType. This is done by consulting the list of configured
- * {@link MediaTypeFileExtensionResolver} instances. Note that some
- * ContentNegotiationStrategy implementations also implement
- * MediaTypeFileExtensionResolver and the class constructor accepting the former
- * will also detect if they implement the latter. If you need to register additional
- * resolvers, you can use the method
- * {@link #addFileExtensionResolvers(MediaTypeFileExtensionResolver...)}.
+ *
Also provides methods to look up file extensions for a media type.
+ * This is done by delegating to the list of configured
+ * {@code MediaTypeFileExtensionResolver} instances.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
-public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
+public class ContentNegotiationManager implements ContentNegotiationStrategy,
+ MediaTypeFileExtensionResolver {
- private static final List MEDIA_TYPE_ALL = Arrays.asList(MediaType.ALL);
+ private static final List MEDIA_TYPE_ALL =
+ Collections.singletonList(MediaType.ALL);
- private final List contentNegotiationStrategies =
+
+ private final List strategies =
new ArrayList();
- private final Set fileExtensionResolvers =
+ private final Set resolvers =
new LinkedHashSet();
/**
- * Create an instance with the given ContentNegotiationStrategy instances.
- * Each instance is checked to see if it is also an implementation of
- * MediaTypeFileExtensionResolver, and if so it is registered as such.
- * @param strategies one more more ContentNegotiationStrategy instances
+ * Create an instance with the given list of
+ * {@code ContentNegotiationStrategy} strategies each of which may also be
+ * an instance of {@code MediaTypeFileExtensionResolver}.
+ * @param strategies the strategies to use
*/
public ContentNegotiationManager(ContentNegotiationStrategy... strategies) {
- Assert.notEmpty(strategies, "At least one ContentNegotiationStrategy is expected");
- this.contentNegotiationStrategies.addAll(Arrays.asList(strategies));
- for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
- if (strategy instanceof MediaTypeFileExtensionResolver) {
- this.fileExtensionResolvers.add((MediaTypeFileExtensionResolver) strategy);
- }
- }
+ this(Arrays.asList(strategies));
}
/**
- * Create an instance with the given ContentNegotiationStrategy instances.
- *
Each instance is checked to see if it is also an implementation of
- * MediaTypeFileExtensionResolver, and if so it is registered as such.
- * @param strategies one more more ContentNegotiationStrategy instances
+ * A collection-based alternative to
+ * {@link #ContentNegotiationManager(ContentNegotiationStrategy...)}.
+ * @param strategies the strategies to use
*/
public ContentNegotiationManager(Collection strategies) {
Assert.notEmpty(strategies, "At least one ContentNegotiationStrategy is expected");
- this.contentNegotiationStrategies.addAll(strategies);
- for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
+ this.strategies.addAll(strategies);
+ for (ContentNegotiationStrategy strategy : this.strategies) {
if (strategy instanceof MediaTypeFileExtensionResolver) {
- this.fileExtensionResolvers.add((MediaTypeFileExtensionResolver) strategy);
+ this.resolvers.add((MediaTypeFileExtensionResolver) strategy);
}
}
}
@@ -100,28 +89,20 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
/**
- * Add MediaTypeFileExtensionResolver instances.
- * Note that some {@link ContentNegotiationStrategy} implementations also
- * implement {@link MediaTypeFileExtensionResolver} and the class constructor
- * accepting the former will also detect implementations of the latter. Therefore
- * you only need to use this method to register additional instances.
- * @param resolvers one or more resolvers
+ * Register more {@code MediaTypeFileExtensionResolver} instances in addition
+ * to those detected at construction.
+ * @param resolvers the resolvers to add
*/
public void addFileExtensionResolvers(MediaTypeFileExtensionResolver... resolvers) {
- this.fileExtensionResolvers.addAll(Arrays.asList(resolvers));
+ this.resolvers.addAll(Arrays.asList(resolvers));
}
- /**
- * Delegate to all configured ContentNegotiationStrategy instances until one
- * returns a non-empty list.
- * @param webRequest the current request
- * @return the requested media types or an empty list, never {@code null}
- * @throws HttpMediaTypeNotAcceptableException if the requested media types cannot be parsed
- */
@Override
- public List resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
- for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
- List mediaTypes = strategy.resolveMediaTypes(webRequest);
+ public List resolveMediaTypes(NativeWebRequest request)
+ throws HttpMediaTypeNotAcceptableException {
+
+ for (ContentNegotiationStrategy strategy : this.strategies) {
+ List mediaTypes = strategy.resolveMediaTypes(request);
if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
continue;
}
@@ -130,27 +111,19 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
return Collections.emptyList();
}
- /**
- * Delegate to all configured MediaTypeFileExtensionResolver instances and aggregate
- * the list of all file extensions found.
- */
@Override
public List resolveFileExtensions(MediaType mediaType) {
Set result = new LinkedHashSet();
- for (MediaTypeFileExtensionResolver resolver : this.fileExtensionResolvers) {
+ for (MediaTypeFileExtensionResolver resolver : this.resolvers) {
result.addAll(resolver.resolveFileExtensions(mediaType));
}
return new ArrayList(result);
}
- /**
- * Delegate to all configured MediaTypeFileExtensionResolver instances and aggregate
- * the list of all known file extensions.
- */
@Override
public List getAllFileExtensions() {
Set result = new LinkedHashSet();
- for (MediaTypeFileExtensionResolver resolver : this.fileExtensionResolvers) {
+ for (MediaTypeFileExtensionResolver resolver : this.resolvers) {
result.addAll(resolver.getAllFileExtensions());
}
return new ArrayList(result);
diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java
index 0c6e6ed1df..289b7b3e71 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java
@@ -130,7 +130,7 @@ public class ContentNegotiationManagerFactoryBean
}
/**
- * Indicate whether to use the Java Activation Framework as a fallback option
+ * Whether to use the Java Activation Framework as a fallback option
* to map from file extensions to media types. This is used only when
* {@link #setFavorPathExtension(boolean)} is set to {@code true}.
* The default value is {@code true}.
@@ -184,8 +184,8 @@ public class ContentNegotiationManagerFactoryBean
* FixedContentNegotiationStrategy}. Alternatively you can also provide a
* custom strategy via {@link #setDefaultContentTypeStrategy}.
*/
- public void setDefaultContentType(MediaType defaultContentType) {
- this.defaultNegotiationStrategy = new FixedContentNegotiationStrategy(defaultContentType);
+ public void setDefaultContentType(MediaType contentType) {
+ this.defaultNegotiationStrategy = new FixedContentNegotiationStrategy(contentType);
}
/**
@@ -195,8 +195,8 @@ public class ContentNegotiationManagerFactoryBean
* provides a simpler alternative to doing the same.
* @since 4.1.2
*/
- public void setDefaultContentTypeStrategy(ContentNegotiationStrategy defaultStrategy) {
- this.defaultNegotiationStrategy = defaultStrategy;
+ public void setDefaultContentTypeStrategy(ContentNegotiationStrategy strategy) {
+ this.defaultNegotiationStrategy = strategy;
}
@Override
@@ -212,7 +212,8 @@ public class ContentNegotiationManagerFactoryBean
if (this.favorPathExtension) {
PathExtensionContentNegotiationStrategy strategy;
if (this.servletContext != null) {
- strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, this.mediaTypes);
+ strategy = new ServletPathExtensionContentNegotiationStrategy(
+ this.servletContext, this.mediaTypes);
}
else {
strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes);
@@ -225,7 +226,8 @@ public class ContentNegotiationManagerFactoryBean
}
if (this.favorParameter) {
- ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes);
+ ParameterContentNegotiationStrategy strategy =
+ new ParameterContentNegotiationStrategy(this.mediaTypes);
strategy.setParameterName(this.parameterName);
strategies.add(strategy);
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java
index 1b1c4b9c04..127f90cef4 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -23,7 +23,7 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * A strategy for resolving the requested media types in a request.
+ * A strategy for resolving the requested media types for a request.
*
* @author Rossen Stoyanchev
* @since 3.2
@@ -37,8 +37,10 @@ public interface ContentNegotiationStrategy {
* @param webRequest the current request
* @return the requested media types or an empty list, never {@code null}
*
- * @throws HttpMediaTypeNotAcceptableException if the requested media types cannot be parsed
+ * @throws HttpMediaTypeNotAcceptableException if the requested media
+ * types cannot be parsed
*/
- List resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException;
+ List resolveMediaTypes(NativeWebRequest webRequest)
+ throws HttpMediaTypeNotAcceptableException;
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java
index f43668c5e8..547ccef791 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -26,30 +26,33 @@ import org.springframework.http.MediaType;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * A ContentNegotiationStrategy that returns a fixed content type.
+ * A {@code ContentNegotiationStrategy} that returns a fixed content type.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class FixedContentNegotiationStrategy implements ContentNegotiationStrategy {
- private static final Log logger = LogFactory.getLog(FixedContentNegotiationStrategy.class);
+ private static final Log logger = LogFactory.getLog(
+ FixedContentNegotiationStrategy.class);
+
+ private final List contentType;
- private final MediaType defaultContentType;
/**
- * Create an instance that always returns the given content type.
+ * Create an instance with the given content type.
*/
- public FixedContentNegotiationStrategy(MediaType defaultContentType) {
- this.defaultContentType = defaultContentType;
+ public FixedContentNegotiationStrategy(MediaType contentType) {
+ this.contentType = Collections.singletonList(contentType);
}
+
@Override
- public List resolveMediaTypes(NativeWebRequest webRequest) {
+ public List resolveMediaTypes(NativeWebRequest request) {
if (logger.isDebugEnabled()) {
- logger.debug("Requested media types is " + this.defaultContentType + " (based on default MediaType)");
+ logger.debug("Requested media types is " + this.contentType + ".");
}
- return Collections.singletonList(this.defaultContentType);
+ return this.contentType;
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java
index 7ed825846e..bcefd7279e 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -19,6 +19,7 @@ package org.springframework.web.accept;
import java.util.Collections;
import java.util.List;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
@@ -26,34 +27,36 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * A ContentNegotiationStrategy that parses the 'Accept' header of the request.
+ * A {@code ContentNegotiationStrategy} that checks the 'Accept' request header.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {
- private static final String ACCEPT_HEADER = "Accept";
/**
* {@inheritDoc}
- * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header cannot be parsed.
+ * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header
+ * cannot be parsed.
*/
@Override
- public List resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
- String acceptHeader = webRequest.getHeader(ACCEPT_HEADER);
+ public List resolveMediaTypes(NativeWebRequest request)
+ throws HttpMediaTypeNotAcceptableException {
+
+ String header = request.getHeader(HttpHeaders.ACCEPT);
+ if (!StringUtils.hasText(header)) {
+ return Collections.emptyList();
+ }
try {
- if (StringUtils.hasText(acceptHeader)) {
- List mediaTypes = MediaType.parseMediaTypes(acceptHeader);
- MediaType.sortBySpecificityAndQuality(mediaTypes);
- return mediaTypes;
- }
+ List mediaTypes = MediaType.parseMediaTypes(header);
+ MediaType.sortBySpecificityAndQuality(mediaTypes);
+ return mediaTypes;
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotAcceptableException(
- "Could not parse accept header [" + acceptHeader + "]: " + ex.getMessage());
+ "Could not parse 'Accept' header [" + header + "]: " + ex.getMessage());
}
- return Collections.emptyList();
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java
index 3fee152081..59125b5ea0 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -31,40 +31,58 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
- * An implementation of {@link MediaTypeFileExtensionResolver} that maintains a lookup
- * from extension to MediaType.
+ * An implementation of {@code MediaTypeFileExtensionResolver} that maintains
+ * lookups between file extensions and MediaTypes in both directions.
+ *
+ * Initially created with a map of file extensions and media types.
+ * Subsequently sub-classes can use {@link #addMapping} to add more mappings.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExtensionResolver {
- private final ConcurrentMap mediaTypes = new ConcurrentHashMap(64);
+ private final ConcurrentMap mediaTypes =
+ new ConcurrentHashMap(64);
- private final MultiValueMap fileExtensions = new LinkedMultiValueMap();
+ private final MultiValueMap fileExtensions =
+ new LinkedMultiValueMap();
private final List allFileExtensions = new LinkedList();
/**
- * Create an instance with the given mappings between extensions and media types.
- * @throws IllegalArgumentException if a media type string cannot be parsed
+ * Create an instance with the given map of file extensions and media types.
*/
public MappingMediaTypeFileExtensionResolver(Map mediaTypes) {
if (mediaTypes != null) {
for (Entry entries : mediaTypes.entrySet()) {
String extension = entries.getKey().toLowerCase(Locale.ENGLISH);
MediaType mediaType = entries.getValue();
- addMapping(extension, mediaType);
+ this.mediaTypes.put(extension, mediaType);
+ this.fileExtensions.add(mediaType, extension);
+ this.allFileExtensions.add(extension);
}
}
}
+ protected List getAllMediaTypes() {
+ return new ArrayList(this.mediaTypes.values());
+ }
+
/**
- * Find the file extensions mapped to the given MediaType.
- * @return 0 or more extensions, never {@code null}
+ * Map an extension to a MediaType. Ignore if extension already mapped.
*/
+ protected void addMapping(String extension, MediaType mediaType) {
+ MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
+ if (previous == null) {
+ this.fileExtensions.add(mediaType, extension);
+ this.allFileExtensions.add(extension);
+ }
+ }
+
+
@Override
public List resolveFileExtensions(MediaType mediaType) {
List fileExtensions = this.fileExtensions.get(mediaType);
@@ -76,27 +94,12 @@ public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExten
return Collections.unmodifiableList(this.allFileExtensions);
}
- protected List getAllMediaTypes() {
- return new ArrayList(this.mediaTypes.values());
- }
-
/**
- * Return the MediaType mapped to the given extension.
+ * Use this method for a reverse lookup from extension to MediaType.
* @return a MediaType for the key or {@code null}
*/
protected MediaType lookupMediaType(String extension) {
return this.mediaTypes.get(extension);
}
- /**
- * Map a MediaType to an extension or ignore if the extensions is already mapped.
- */
- protected void addMapping(String extension, MediaType mediaType) {
- MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
- if (previous == null) {
- this.fileExtensions.add(mediaType, extension);
- this.allFileExtensions.add(extension);
- }
- }
-
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java
index 218a07fe6c..c063e5c50b 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -21,8 +21,8 @@ import java.util.List;
import org.springframework.http.MediaType;
/**
- * A strategy for resolving a {@link MediaType} to one or more path extensions.
- * For example "application/json" to "json".
+ * Strategy to resolve {@link MediaType} to a list of file extensions.
+ * For example resolve "application/json" to "json".
*
* @author Rossen Stoyanchev
* @since 3.2
@@ -38,7 +38,7 @@ public interface MediaTypeFileExtensionResolver {
List resolveFileExtensions(MediaType mediaType);
/**
- * Return all known file extensions.
+ * Return all registered file extensions.
* @return a list of extensions or an empty list, never {@code null}
*/
List getAllFileExtensions();
diff --git a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java
index cd70681276..a2520d27fd 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -27,51 +27,61 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * A ContentNegotiationStrategy that uses a request parameter to determine what
- * media types are requested. The default parameter name is {@code format}.
- * Its value is used to look up the media type in the map given to the constructor.
- *
+ * A {@code ContentNegotiationStrategy} that resolves a query parameter to a
+ * key to be used to look up a media type. The default parameter name is
+ * {@code format}.
+ *s
* @author Rossen Stoyanchev
* @since 3.2
*/
-public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
+public class ParameterContentNegotiationStrategy
+ extends AbstractMappingContentNegotiationStrategy {
- private static final Log logger = LogFactory.getLog(ParameterContentNegotiationStrategy.class);
+ private static final Log logger = LogFactory.getLog(
+ ParameterContentNegotiationStrategy.class);
private String parameterName = "format";
+
/**
- * Create an instance with the given extension-to-MediaType lookup.
- * @throws IllegalArgumentException if a media type string cannot be parsed
+ * Create an instance with the given map of file extensions and media types.
*/
public ParameterContentNegotiationStrategy(Map mediaTypes) {
super(mediaTypes);
}
+
/**
- * Set the parameter name that can be used to determine the requested media type.
- * The default parameter name is {@code format}.
+ * Set the name of the parameter to use to determine requested media types.
+ *
By default this is set to {@code "format"}.
*/
public void setParameterName(String parameterName) {
Assert.notNull(parameterName, "parameterName is required");
this.parameterName = parameterName;
}
+ public String getParameterName() {
+ return this.parameterName;
+ }
+
@Override
- protected String getMediaTypeKey(NativeWebRequest webRequest) {
- return webRequest.getParameter(this.parameterName);
+ protected String getMediaTypeKey(NativeWebRequest request) {
+ return request.getParameter(getParameterName());
}
@Override
protected void handleMatch(String mediaTypeKey, MediaType mediaType) {
if (logger.isDebugEnabled()) {
- logger.debug("Requested media type is '" + mediaType + "' (based on parameter '" +
- this.parameterName + "'='" + mediaTypeKey + "')");
+ logger.debug("Requested media type is '" + mediaType +
+ "' based on '" + getParameterName() + "'='" + mediaTypeKey + "'.");
}
}
@Override
- protected MediaType handleNoMatch(NativeWebRequest request, String key) throws HttpMediaTypeNotAcceptableException {
+ protected MediaType handleNoMatch(NativeWebRequest request, String key)
+ throws HttpMediaTypeNotAcceptableException {
+
throw new HttpMediaTypeNotAcceptableException(getAllMediaTypes());
}
+
}
diff --git a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java
index de98ae2ec3..a64292823a 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -38,30 +38,32 @@ import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils;
/**
- * A ContentNegotiationStrategy that uses the path extension of the URL to
- * determine what media types are requested. The path extension is first looked
- * up in the map of media types provided to the constructor. If that fails, the
- * Java Activation framework is used as a fallback mechanism.
+ * A {@code ContentNegotiationStrategy} that resolves the file extension in the
+ * request path to a key to be used to look up a media type.
*
- *
- * The presence of the Java Activation framework is detected and enabled
- * automatically but the {@link #setUseJaf(boolean)} property may be used to
- * override that setting.
+ *
If the file extension is not found in the explicit registrations provided
+ * to the constructor, the Java Activation Framework (JAF) is used as a fallback
+ * mechanism.
+ *
+ *
The presence of the JAF is detected and enabled automatically but the
+ * {@link #setUseJaf(boolean)} property may be set to false.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
-public class PathExtensionContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
-
- private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap",
- PathExtensionContentNegotiationStrategy.class.getClassLoader());
+public class PathExtensionContentNegotiationStrategy
+ extends AbstractMappingContentNegotiationStrategy {
private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class);
- private static final UrlPathHelper urlPathHelper = new UrlPathHelper();
+ private static final boolean JAF_PRESENT = ClassUtils.isPresent(
+ "javax.activation.FileTypeMap",
+ PathExtensionContentNegotiationStrategy.class.getClassLoader());
+
+ private static final UrlPathHelper PATH_HELPER = new UrlPathHelper();
static {
- urlPathHelper.setUrlDecode(false);
+ PATH_HELPER.setUrlDecode(false);
}
private boolean useJaf = JAF_PRESENT;
@@ -70,8 +72,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
/**
- * Create an instance with the given extension-to-MediaType lookup.
- * @throws IllegalArgumentException if a media type string cannot be parsed
+ * Create an instance with the given map of file extensions and media types.
*/
public PathExtensionContentNegotiationStrategy(Map mediaTypes) {
super(mediaTypes);
@@ -87,21 +88,17 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
/**
- * Indicate whether to use the Java Activation Framework to map from file
- * extensions to media types.
- *
- * Default is {@code true}, i.e. the Java Activation Framework is used
- * (if available).
+ * Whether to use the Java Activation Framework to look up file extensions.
+ *
By default if this property is not set JAF is present on the
+ * classpath it will be used.
*/
public void setUseJaf(boolean useJaf) {
this.useJaf = useJaf;
}
/**
- * Whether to ignore requests that have a file extension that does not match
- * any mapped media types. Setting this to {@code false} will result in a
- * {@code HttpMediaTypeNotAcceptableException}.
- *
+ * Whether to ignore requests with unknown file extension. Setting this to
+ * {@code false} results in {@code HttpMediaTypeNotAcceptableException}.
*
By default this is set to {@code true}.
*/
public void setIgnoreUnknownExtensions(boolean ignoreUnknownExtensions) {
@@ -111,35 +108,31 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
@Override
protected String getMediaTypeKey(NativeWebRequest webRequest) {
- HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
- if (servletRequest == null) {
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ if (request == null) {
logger.warn("An HttpServletRequest is required to determine the media type key");
return null;
}
- String path = urlPathHelper.getLookupPathForRequest(servletRequest);
+ String path = PATH_HELPER.getLookupPathForRequest(request);
String filename = WebUtils.extractFullFilenameFromUrlPath(path);
String extension = StringUtils.getFilenameExtension(filename);
return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null;
}
- @Override
- protected void handleMatch(String extension, MediaType mediaType) {
- }
-
@Override
protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
throws HttpMediaTypeNotAcceptableException {
if (this.useJaf) {
- MediaType jafMediaType = JafMediaTypeFactory.getMediaType("file." + extension);
- if (jafMediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(jafMediaType)) {
- return jafMediaType;
+ MediaType mediaType = JafMediaTypeFactory.getMediaType("file." + extension);
+ if (mediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) {
+ return mediaType;
}
}
- if (!this.ignoreUnknownExtensions) {
- throw new HttpMediaTypeNotAcceptableException(getAllMediaTypes());
+ if (this.ignoreUnknownExtensions) {
+ return null;
}
- return null;
+ throw new HttpMediaTypeNotAcceptableException(getAllMediaTypes());
}
@@ -161,7 +154,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
Resource resource = new ClassPathResource("org/springframework/mail/javamail/mime.types");
if (resource.exists()) {
if (logger.isTraceEnabled()) {
- logger.trace("Loading Java Activation Framework FileTypeMap from " + resource);
+ logger.trace("Loading JAF FileTypeMap from " + resource);
}
InputStream inputStream = null;
try {
diff --git a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java
index 01aa7cda5d..203387105c 100644
--- a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java
+++ b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -25,43 +25,42 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.context.request.NativeWebRequest;
/**
- * An extension of {@code PathExtensionContentNegotiationStrategy} that uses
- * {@link ServletContext#getMimeType(String)} as a fallback mechanism when
- * matching a path extension to a media type.
+ * Extends {@code PathExtensionContentNegotiationStrategy} that also uses
+ * {@link ServletContext#getMimeType(String)} to resolve file extensions.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
-public class ServletPathExtensionContentNegotiationStrategy extends PathExtensionContentNegotiationStrategy {
+public class ServletPathExtensionContentNegotiationStrategy
+ extends PathExtensionContentNegotiationStrategy {
private final ServletContext servletContext;
/**
* Create an instance with the given extension-to-MediaType lookup.
- * @throws IllegalArgumentException if a media type string cannot be parsed
*/
- public ServletPathExtensionContentNegotiationStrategy(
- ServletContext servletContext, Map mediaTypes) {
+ public ServletPathExtensionContentNegotiationStrategy(ServletContext context,
+ Map mediaTypes) {
super(mediaTypes);
- Assert.notNull(servletContext, "ServletContext is required!");
- this.servletContext = servletContext;
+ Assert.notNull(context, "ServletContext is required!");
+ this.servletContext = context;
}
/**
* Create an instance without any mappings to start with. Mappings may be
- * added later on if any extensions are resolved through
- * {@link ServletContext#getMimeType(String)} or through the Java Activation
- * framework.
+ * added later when extensions are resolved through
+ * {@link ServletContext#getMimeType(String)} or via JAF.
*/
- public ServletPathExtensionContentNegotiationStrategy(ServletContext servletContext) {
- this(servletContext, null);
+ public ServletPathExtensionContentNegotiationStrategy(ServletContext context) {
+ this(context, null);
}
+
/**
- * Look up the given extension via {@link ServletContext#getMimeType(String)}
- * and if that doesn't help, delegate to the parent implementation.
+ * Resolve file extension via {@link ServletContext#getMimeType(String)}
+ * and also delegate to base class for a potential JAF lookup.
*/
@Override
protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java
index ff34257ada..35a928e2b9 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -25,7 +25,7 @@ import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
import org.springframework.web.accept.ContentNegotiationStrategy;
/**
- * Helps with configuring a {@link ContentNegotiationManager}.
+ * Help to create and configure a {@link ContentNegotiationManager}.
*
* By default strategies for checking the extension of the request path and
* the {@code Accept} header are registered. The path extension check will perform
@@ -37,7 +37,8 @@ import org.springframework.web.accept.ContentNegotiationStrategy;
*/
public class ContentNegotiationConfigurer {
- private final ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
+ private final ContentNegotiationManagerFactoryBean factoryBean =
+ new ContentNegotiationManagerFactoryBean();
private final Map mediaTypes = new HashMap();