11/*
2- * Copyright 2002-2010 the original author or authors.
2+ * Copyright 2002-2011 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.
2929import java .util .Set ;
3030import java .util .concurrent .ConcurrentHashMap ;
3131import java .util .concurrent .ConcurrentMap ;
32-
3332import javax .activation .FileTypeMap ;
3433import javax .activation .MimetypesFileTypeMap ;
3534import javax .servlet .ServletContext ;
3837
3938import org .apache .commons .logging .Log ;
4039import org .apache .commons .logging .LogFactory ;
40+
4141import org .springframework .beans .factory .BeanFactoryUtils ;
4242import org .springframework .core .OrderComparator ;
4343import org .springframework .core .Ordered ;
7070 * <p>This view resolver uses the requested {@linkplain MediaType media type} to select a suitable {@link View} for a
7171 * request. This media type is determined by using the following criteria:
7272 * <ol>
73- * <li>If the requested path has a file extension and if the {@link #setFavorPathExtension(boolean) } property is
73+ * <li>If the requested path has a file extension and if the {@link #setFavorPathExtension} property is
7474 * {@code true}, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching media type.</li>
75- * <li>If the request contains a parameter defining the extension and if the {@link #setFavorParameter(boolean) }
75+ * <li>If the request contains a parameter defining the extension and if the {@link #setFavorParameter}
7676 * property is <code>true</code>, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching
7777 * media type. The default name of the parameter is <code>format</code> and it can be configured using the
7878 * {@link #setParameterName(String) parameterName} property.</li>
7979 * <li>If there is no match in the {@link #setMediaTypes(Map) mediaTypes} property and if the Java Activation
80- * Framework (JAF) is both {@linkplain #setUseJaf(boolean) enabled} and present on the class path,
80+ * Framework (JAF) is both {@linkplain #setUseJaf enabled} and present on the class path,
8181 * {@link FileTypeMap#getContentType(String)} is used instead.</li>
8282 * <li>If the previous steps did not result in a media type, and
83- * {@link #setIgnoreAcceptHeader(boolean) ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is
83+ * {@link #setIgnoreAcceptHeader ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is
8484 * used.</li>
8585 * </ol>
8686 *
@@ -149,7 +149,7 @@ public int getOrder() {
149149 }
150150
151151 /**
152- * Indicates whether the extension of the request path should be used to determine the requested media type,
152+ * Indicate whether the extension of the request path should be used to determine the requested media type,
153153 * in favor of looking at the {@code Accept} header. The default value is {@code true}.
154154 * <p>For instance, when this flag is <code>true</code> (the default), a request for {@code /hotels.pdf}
155155 * will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the
@@ -160,7 +160,7 @@ public void setFavorPathExtension(boolean favorPathExtension) {
160160 }
161161
162162 /**
163- * Indicates whether a request parameter should be used to determine the requested media type,
163+ * Indicate whether a request parameter should be used to determine the requested media type,
164164 * in favor of looking at the {@code Accept} header. The default value is {@code false}.
165165 * <p>For instance, when this flag is <code>true</code>, a request for {@code /hotels?format=pdf} will result
166166 * in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined
@@ -171,39 +171,38 @@ public void setFavorParameter(boolean favorParameter) {
171171 }
172172
173173 /**
174- * Sets the parameter name that can be used to determine the requested media type if the {@link
175- * #setFavorParameter(boolean) } property is {@code true}. The default parameter name is {@code format}.
174+ * Set the parameter name that can be used to determine the requested media type if the {@link
175+ * #setFavorParameter} property is {@code true}. The default parameter name is {@code format}.
176176 */
177177 public void setParameterName (String parameterName ) {
178178 this .parameterName = parameterName ;
179179 }
180180
181181 /**
182- * Indicates whether the HTTP {@code Accept} header should be ignored. Default is {@code false}.
183- * If set to {@code true}, this view resolver will only refer to the file extension and/or paramter,
184- * as indicated by the {@link #setFavorPathExtension(boolean) favorPathExtension} and
185- * {@link #setFavorParameter(boolean) favorParameter} properties.
182+ * Indicate whether the HTTP {@code Accept} header should be ignored. Default is {@code false}.
183+ * <p> If set to {@code true}, this view resolver will only refer to the file extension and/or
184+ * parameter, as indicated by the {@link #setFavorPathExtension favorPathExtension} and
185+ * {@link #setFavorParameter favorParameter} properties.
186186 */
187187 public void setIgnoreAcceptHeader (boolean ignoreAcceptHeader ) {
188188 this .ignoreAcceptHeader = ignoreAcceptHeader ;
189189 }
190190
191191 /**
192- * Indicates whether a {@link HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable} status code should be
193- * returned if no suitable view can be found.
194- *
192+ * Indicate whether a {@link HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable}
193+ * status code should be returned if no suitable view can be found.
195194 * <p>Default is {@code false}, meaning that this view resolver returns {@code null} for
196- * {@link #resolveViewName(String, Locale)} when an acceptable view cannot be found. This will allow for view
197- * resolvers chaining. When this property is set to {@code true},
198- * {@link #resolveViewName(String, Locale)} will respond with a view that sets the response status to
199- * {@code 406 Not Acceptable} instead.
195+ * {@link #resolveViewName(String, Locale)} when an acceptable view cannot be found.
196+ * This will allow for view resolvers chaining. When this property is set to {@code true},
197+ * {@link #resolveViewName(String, Locale)} will respond with a view that sets the
198+ * response status to {@code 406 Not Acceptable} instead.
200199 */
201200 public void setUseNotAcceptableStatusCode (boolean useNotAcceptableStatusCode ) {
202201 this .useNotAcceptableStatusCode = useNotAcceptableStatusCode ;
203202 }
204203
205204 /**
206- * Sets the mapping from file extensions to media types.
205+ * Set the mapping from file extensions to media types.
207206 * <p>When this mapping is not set or when an extension is not present, this view resolver
208207 * will fall back to using a {@link FileTypeMap} when the Java Action Framework is available.
209208 */
@@ -217,15 +216,15 @@ public void setMediaTypes(Map<String, String> mediaTypes) {
217216 }
218217
219218 /**
220- * Sets the default views to use when a more specific view can not be obtained
219+ * Set the default views to use when a more specific view can not be obtained
221220 * from the {@link ViewResolver} chain.
222221 */
223222 public void setDefaultViews (List <View > defaultViews ) {
224223 this .defaultViews = defaultViews ;
225224 }
226225
227226 /**
228- * Sets the default content type.
227+ * Set the default content type.
229228 * <p>This content type will be used when file extension, parameter, nor {@code Accept}
230229 * header define a content-type, either through being disabled or empty.
231230 */
@@ -234,7 +233,7 @@ public void setDefaultContentType(MediaType defaultContentType) {
234233 }
235234
236235 /**
237- * Indicates whether to use the Java Activation Framework to map from file extensions to media types.
236+ * Indicate whether to use the Java Activation Framework to map from file extensions to media types.
238237 * <p>Default is {@code true}, i.e. the Java Activation Framework is used (if available).
239238 */
240239 public void setUseJaf (boolean useJaf ) {
@@ -252,10 +251,8 @@ public void setViewResolvers(List<ViewResolver> viewResolvers) {
252251
253252 @ Override
254253 protected void initServletContext (ServletContext servletContext ) {
255-
256254 Collection <ViewResolver > matchingBeans =
257- BeanFactoryUtils .beansOfTypeIncludingAncestors (getApplicationContext (), ViewResolver .class ).values ();
258-
255+ BeanFactoryUtils .beansOfTypeIncludingAncestors (getApplicationContext (), ViewResolver .class ).values ();
259256 if (this .viewResolvers == null ) {
260257 this .viewResolvers = new ArrayList <ViewResolver >(matchingBeans .size ());
261258 for (ViewResolver viewResolver : matchingBeans ) {
@@ -281,13 +278,38 @@ protected void initServletContext(ServletContext servletContext) {
281278 OrderComparator .sort (this .viewResolvers );
282279 }
283280
281+ public View resolveViewName (String viewName , Locale locale ) throws Exception {
282+ RequestAttributes attrs = RequestContextHolder .getRequestAttributes ();
283+ Assert .isInstanceOf (ServletRequestAttributes .class , attrs );
284+ List <MediaType > requestedMediaTypes = getMediaTypes (((ServletRequestAttributes ) attrs ).getRequest ());
285+ if (requestedMediaTypes != null ) {
286+ List <View > candidateViews = getCandidateViews (viewName , locale , requestedMediaTypes );
287+ View bestView = getBestView (candidateViews , requestedMediaTypes );
288+ if (bestView != null ) {
289+ return bestView ;
290+ }
291+ }
292+ if (this .useNotAcceptableStatusCode ) {
293+ if (logger .isDebugEnabled ()) {
294+ logger .debug ("No acceptable view found; returning 406 (Not Acceptable) status code" );
295+ }
296+ return NOT_ACCEPTABLE_VIEW ;
297+ }
298+ else {
299+ if (logger .isDebugEnabled ()) {
300+ logger .debug ("No acceptable view found; returning null" );
301+ }
302+ return null ;
303+ }
304+ }
305+
284306 /**
285307 * Determines the list of {@link MediaType} for the given {@link HttpServletRequest}.
286308 * <p>The default implementation invokes {@link #getMediaTypeFromFilename(String)} if {@linkplain
287- * #setFavorPathExtension(boolean) favorPathExtension} property is <code>true</code>. If the property is
288- * <code>false</code>, or when a media type cannot be determined from the request path, this method will
289- * inspect the {@code Accept} header of the request.
290- * <p>This method can be overriden to provide a different algorithm.
309+ * #setFavorPathExtension favorPathExtension} property is <code>true</code>. If the property is
310+ * <code>false</code>, or when a media type cannot be determined from the request path,
311+ * this method will inspect the {@code Accept} header of the request.
312+ * <p>This method can be overridden to provide a different algorithm.
291313 * @param request the current servlet request
292314 * @return the list of media types requested, if any
293315 */
@@ -319,26 +341,29 @@ protected List<MediaType> getMediaTypes(HttpServletRequest request) {
319341 if (!this .ignoreAcceptHeader ) {
320342 String acceptHeader = request .getHeader (ACCEPT_HEADER );
321343 if (StringUtils .hasText (acceptHeader )) {
322- List <MediaType > acceptableMediaTypes = MediaType .parseMediaTypes (acceptHeader );
323- List <MediaType > producibleMediaTypes = getProducibleMediaTypes (request );
324-
325- Set <MediaType > compatibleMediaTypes = new LinkedHashSet <MediaType >();
326- for (MediaType a : acceptableMediaTypes ) {
327- for (MediaType p : producibleMediaTypes ) {
328- if (a .isCompatibleWith (p )) {
329- compatibleMediaTypes .add (getMostSpecificMediaType (a , p ));
344+ try {
345+ List <MediaType > acceptableMediaTypes = MediaType .parseMediaTypes (acceptHeader );
346+ List <MediaType > producibleMediaTypes = getProducibleMediaTypes (request );
347+ Set <MediaType > compatibleMediaTypes = new LinkedHashSet <MediaType >();
348+ for (MediaType acceptable : acceptableMediaTypes ) {
349+ for (MediaType producible : producibleMediaTypes ) {
350+ if (acceptable .isCompatibleWith (producible )) {
351+ compatibleMediaTypes .add (getMostSpecificMediaType (acceptable , producible ));
352+ }
330353 }
331354 }
355+ List <MediaType > mediaTypes = new ArrayList <MediaType >(compatibleMediaTypes );
356+ MediaType .sortByQualityValue (mediaTypes );
357+ if (logger .isDebugEnabled ()) {
358+ logger .debug ("Requested media types are " + mediaTypes + " based on Accept header types " +
359+ "and producible media types " + producibleMediaTypes + ")" );
360+ }
361+ return mediaTypes ;
332362 }
333-
334- List <MediaType > mediaTypes = new ArrayList <MediaType >(compatibleMediaTypes );
335- MediaType .sortByQualityValue (mediaTypes );
336-
337- if (logger .isDebugEnabled ()) {
338- logger .debug ("Requested media types are " + mediaTypes + " based on Accept header types " +
339- "and producible media types " + producibleMediaTypes + ")" );
363+ catch (IllegalArgumentException ex ) {
364+ logger .debug ("Could not parse accept header [" + acceptHeader + "]: " + ex .getMessage ());
365+ return null ;
340366 }
341- return mediaTypes ;
342367 }
343368 }
344369 if (this .defaultContentType != null ) {
@@ -355,7 +380,8 @@ protected List<MediaType> getMediaTypes(HttpServletRequest request) {
355380
356381 @ SuppressWarnings ("unchecked" )
357382 private List <MediaType > getProducibleMediaTypes (HttpServletRequest request ) {
358- Set <MediaType > mediaTypes = (Set <MediaType >) request .getAttribute (HandlerMapping .PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE );
383+ Set <MediaType > mediaTypes = (Set <MediaType >)
384+ request .getAttribute (HandlerMapping .PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE );
359385 if (!CollectionUtils .isEmpty (mediaTypes )) {
360386 return new ArrayList <MediaType >(mediaTypes );
361387 }
@@ -412,31 +438,6 @@ protected MediaType getMediaTypeFromParameter(String parameterValue) {
412438 return this .mediaTypes .get (parameterValue .toLowerCase (Locale .ENGLISH ));
413439 }
414440
415- public View resolveViewName (String viewName , Locale locale ) throws Exception {
416- RequestAttributes attrs = RequestContextHolder .getRequestAttributes ();
417- Assert .isInstanceOf (ServletRequestAttributes .class , attrs );
418- List <MediaType > requestedMediaTypes = getMediaTypes (((ServletRequestAttributes ) attrs ).getRequest ());
419- List <View > candidateViews = getCandidateViews (viewName , locale , requestedMediaTypes );
420- View bestView = getBestView (candidateViews , requestedMediaTypes );
421- if (bestView != null ) {
422- return bestView ;
423- }
424- else {
425- if (this .useNotAcceptableStatusCode ) {
426- if (logger .isDebugEnabled ()) {
427- logger .debug ("No acceptable view found; returning 406 (Not Acceptable) status code" );
428- }
429- return NOT_ACCEPTABLE_VIEW ;
430- }
431- else {
432- if (logger .isDebugEnabled ()) {
433- logger .debug ("No acceptable view found; returning null" );
434- }
435- return null ;
436- }
437- }
438- }
439-
440441 private List <View > getCandidateViews (String viewName , Locale locale , List <MediaType > requestedMediaTypes )
441442 throws Exception {
442443
@@ -466,7 +467,7 @@ private List<View> getCandidateViews(String viewName, Locale locale, List<MediaT
466467
467468 private List <String > getExtensionsForMediaType (MediaType requestedMediaType ) {
468469 List <String > result = new ArrayList <String >();
469- for (Entry <String , MediaType > entry : mediaTypes .entrySet ()) {
470+ for (Entry <String , MediaType > entry : this . mediaTypes .entrySet ()) {
470471 if (requestedMediaType .includes (entry .getValue ())) {
471472 result .add (entry .getKey ());
472473 }
@@ -490,9 +491,8 @@ private View getBestView(List<View> candidateViews, List<MediaType> requestedMed
490491 }
491492 if (bestView != null ) {
492493 if (logger .isDebugEnabled ()) {
493- logger .debug (
494- "Returning [" + bestView + "] based on requested media type '" + bestRequestedMediaType +
495- "'" );
494+ logger .debug ("Returning [" + bestView + "] based on requested media type '" +
495+ bestRequestedMediaType + "'" );
496496 }
497497 break ;
498498 }
@@ -547,7 +547,7 @@ private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
547547
548548 public static MediaType getMediaType (String fileName ) {
549549 String mediaType = fileTypeMap .getContentType (fileName );
550- return StringUtils .hasText (mediaType ) ? MediaType .parseMediaType (mediaType ) : null ;
550+ return ( StringUtils .hasText (mediaType ) ? MediaType .parseMediaType (mediaType ) : null ) ;
551551 }
552552 }
553553
@@ -558,8 +558,7 @@ public String getContentType() {
558558 return null ;
559559 }
560560
561- public void render (Map <String , ?> model , HttpServletRequest request , HttpServletResponse response )
562- throws Exception {
561+ public void render (Map <String , ?> model , HttpServletRequest request , HttpServletResponse response ) {
563562 response .setStatus (HttpServletResponse .SC_NOT_ACCEPTABLE );
564563 }
565564 };
0 commit comments