diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 904a3fd2bd..b5dc22e5f4 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -575,7 +574,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i /** * MethodFilter that matches {@link InitBinder @InitBinder} methods. */ - public static MethodFilter INIT_BINDER_METHODS = new MethodFilter() { + public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() { public boolean matches(Method method) { return AnnotationUtils.findAnnotation(method, InitBinder.class) != null; @@ -585,7 +584,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i /** * MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods. */ - public static MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() { + public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() { public boolean matches(Method method) { return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 34e10b9e9e..66e794e28a 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.springframework.core.annotation.AnnotationUtils; @@ -33,6 +34,9 @@ import org.springframework.stereotype.Controller; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -46,9 +50,9 @@ import org.springframework.web.servlet.handler.MappedInterceptors; import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; /** - * An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and - * the lookup of {@link HandlerMethod}s. - * + * An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and the + * lookup of {@link HandlerMethod}s. + * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1.0 @@ -84,10 +88,10 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping beanType) { @@ -106,9 +110,8 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMappingOnly {@link RequestMapping @RequestMapping}-annotated methods are considered. - * Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their + * Provides a {@link RequestMappingInfo} for the given method.

Only {@link RequestMapping @RequestMapping}-annotated + * methods are considered. Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their * attributes combined with method-level {@link RequestMapping @RequestMapping} attributes. * * @param method the method to create a mapping for @@ -137,14 +140,13 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping getMappingPaths(RequestMappingInfo mapping) { return mapping.getPatterns(); @@ -152,10 +154,13 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping requestMappingInfos, String lookupPath, HttpServletRequest request) - throws HttpRequestMethodNotSupportedException { + protected HandlerMethod handleNoMatch(Set requestMappingInfos, + String lookupPath, + HttpServletRequest request) throws ServletException { Set allowedMethods = new HashSet(6); + Set consumableMediaTypes = new HashSet(); + Set producibleMediaTypes = new HashSet(); for (RequestMappingInfo info : requestMappingInfos) { - for (String pattern : info.getPatterns()) { - if (pathMatcher.match(pattern, lookupPath)) { - for (RequestMethod method : info.getMethods().getMethods()) { - allowedMethods.add(method.name()); - } + if (!info.getMethods().match(request)) { + for (RequestMethod method : info.getMethods().getMethods()) { + allowedMethods.add(method.name()); } } + if (!info.getConsumes().match(request)) { + consumableMediaTypes.addAll(info.getConsumes().getMediaTypes()); + } + if (!info.getProduces().match(request)) { + producibleMediaTypes.addAll(info.getProduces().getMediaTypes()); + } } if (!allowedMethods.isEmpty()) { - throw new HttpRequestMethodNotSupportedException(request.getMethod(), - allowedMethods.toArray(new String[allowedMethods.size()])); - - } else { + throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods); + } + else if (!consumableMediaTypes.isEmpty()) { + MediaType contentType = null; + if (StringUtils.hasLength(request.getContentType())) { + contentType = MediaType.parseMediaType(request.getContentType()); + } + throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList(consumableMediaTypes)); + } + else if (!producibleMediaTypes.isEmpty()) { + throw new HttpMediaTypeNotAcceptableException(new ArrayList(producibleMediaTypes)); + } + else { return null; } } @@ -218,13 +240,13 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMappingFurthermore, the following assumptions are made about the input RequestMappings: - *

+ *

Furthermore, the following assumptions are made about the input RequestMappings:

* * @see RequestMappingHandlerMapping#getMatchingMapping(RequestMappingInfo, String, HttpServletRequest) */ @@ -289,26 +311,6 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping accept, List otherAccept) { - for (MediaType requestAccept : this.requestAcceptHeader) { - int pos1 = indexOfIncluded(requestAccept, accept); - int pos2 = indexOfIncluded(requestAccept, otherAccept); - if (pos1 != pos2) { - return pos2 - pos1; - } - } - return 0; - } - - private int indexOfIncluded(MediaType requestAccept, List accept) { - for (int i = 0; i < accept.size(); i++) { - if (requestAccept.includes(accept.get(i))) { - return i; - } - } - return -1; - } - } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java index 3cfd9c0f09..a70904f319 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java @@ -17,30 +17,38 @@ package org.springframework.web.servlet.mvc.method.annotation.support; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; /** - * A base class for resolving method argument values by reading from the body of a request with - * {@link HttpMessageConverter}s and for handling method return values by writing to the response with - * {@link HttpMessageConverter}s. - * + * A base class for resolving method argument values by reading from the body of a request with {@link + * HttpMessageConverter}s and for handling method return values by writing to the response with {@link + * HttpMessageConverter}s. + * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1 @@ -48,103 +56,161 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; public abstract class AbstractMessageConverterMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { + private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); + protected final Log logger = LogFactory.getLog(getClass()); private final List> messageConverters; + private final List allSupportedMediaTypes; + protected AbstractMessageConverterMethodProcessor(List> messageConverters) { - Assert.notNull(messageConverters, "'messageConverters' must not be null"); + Assert.notEmpty(messageConverters, "'messageConverters' must not be empty"); this.messageConverters = messageConverters; + this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters); + } + + private static List getAllSupportedMediaTypes(List> messageConverters) { + Set allSupportedMediaTypes = new HashSet(); + for (HttpMessageConverter messageConverter : messageConverters) { + allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); + } + List result = new ArrayList(allSupportedMediaTypes); + MediaType.sortBySpecificity(result); + return Collections.unmodifiableList(result); } @SuppressWarnings("unchecked") protected Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam, - Class paramType) + Class paramType) throws IOException, HttpMediaTypeNotSupportedException { - + HttpInputMessage inputMessage = createInputMessage(webRequest); MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType == null) { - StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType())); - String paramName = methodParam.getParameterName(); - if (paramName != null) { - builder.append(' '); - builder.append(paramName); - } - throw new HttpMediaTypeNotSupportedException("Cannot read parameter (" + builder.toString() + - ") using HttpMessageConverters: no Content-Type found in HTTP request"); + contentType = MediaType.APPLICATION_OCTET_STREAM; } - List allSupportedMediaTypes = new ArrayList(); - if (this.messageConverters != null) { - for (HttpMessageConverter messageConverter : this.messageConverters) { - allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); - if (messageConverter.canRead(paramType, contentType)) { - if (logger.isDebugEnabled()) { - logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" + - messageConverter + "]"); - } - return ((HttpMessageConverter) messageConverter).read(paramType, inputMessage); + for (HttpMessageConverter messageConverter : this.messageConverters) { + if (messageConverter.canRead(paramType, contentType)) { + if (logger.isDebugEnabled()) { + logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" + + messageConverter + "]"); } + return ((HttpMessageConverter) messageConverter).read(paramType, inputMessage); } } - + throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes); } - protected abstract HttpInputMessage createInputMessage(NativeWebRequest webRequest); - - protected void writeWithMessageConverters(NativeWebRequest webRequest, Object returnValue) - throws IOException, HttpMediaTypeNotAcceptableException { - writeWithMessageConverters(returnValue, createInputMessage(webRequest), createOutputMessage(webRequest)); + /** + * Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}. + * + * @param webRequest the web request to create an input message from + * @return the input message + */ + protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) { + HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); + return new ServletServerHttpRequest(servletRequest); } - protected abstract HttpOutputMessage createOutputMessage(NativeWebRequest webRequest); + /** + * Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}. + * + * @param webRequest the web request to create an output message from + * @return the output message + */ + protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) { + HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class); + return new ServletServerHttpResponse(servletResponse); + } @SuppressWarnings("unchecked") - protected void writeWithMessageConverters(T returnValue, - HttpInputMessage inputMessage, + protected void writeWithMessageConverters(T returnValue, + MethodParameter returnType, + HttpInputMessage inputMessage, HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { - - List acceptedMediaTypes = getAcceptedMediaTypes(inputMessage); - List allSupportedMediaTypes = new ArrayList(); - if (this.messageConverters != null) { - for (MediaType acceptedMediaType : acceptedMediaTypes) { - for (HttpMessageConverter messageConverter : this.messageConverters) { - if (!messageConverter.canWrite(returnValue.getClass(), acceptedMediaType)) { - continue; - } - ((HttpMessageConverter) messageConverter).write(returnValue, acceptedMediaType, outputMessage); + Set producibleMediaTypes = getProducibleMediaTypes(returnType.getMethod(), returnValue.getClass()); + Set acceptableMediaTypes = getAcceptableMediaTypes(inputMessage); + + List mediaTypes = new ArrayList(); + for (MediaType acceptableMediaType : acceptableMediaTypes) { + for (MediaType producibleMediaType : producibleMediaTypes) { + if (acceptableMediaType.isCompatibleWith(producibleMediaType)) { + mediaTypes.add(getMostSpecificMediaType(acceptableMediaType, producibleMediaType)); + } + } + } + if (mediaTypes.isEmpty()) { + throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); + } + MediaType.sortBySpecificity(mediaTypes); + MediaType selectedMediaType = null; + for (MediaType mediaType : mediaTypes) { + if (mediaType.isConcrete()) { + selectedMediaType = mediaType; + break; + } + else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { + selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; + } + } + if (selectedMediaType != null) { + for (HttpMessageConverter messageConverter : messageConverters) { + if (messageConverter.canWrite(returnValue.getClass(), selectedMediaType)) { + ((HttpMessageConverter) messageConverter).write(returnValue, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { - MediaType contentType = outputMessage.getHeaders().getContentType(); - if (contentType == null) { - contentType = acceptedMediaType; - } - logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + + logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); } return; } } - for (HttpMessageConverter messageConverter : messageConverters) { - allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); - } } - throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); + else { + throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); + } } - private List getAcceptedMediaTypes(HttpInputMessage inputMessage) { - List acceptedMediaTypes = inputMessage.getHeaders().getAccept(); - if (acceptedMediaTypes.isEmpty()) { - acceptedMediaTypes = Collections.singletonList(MediaType.ALL); + private Set getProducibleMediaTypes(Method handlerMethod, Class returnValueClass) { + RequestMapping requestMappingAnn = handlerMethod.getAnnotation(RequestMapping.class); + if (requestMappingAnn == null) { + requestMappingAnn = handlerMethod.getClass().getAnnotation(RequestMapping.class); } + Set result = new HashSet(); + if (requestMappingAnn != null) { + for (String produce : requestMappingAnn.produces()) { + result.add(MediaType.parseMediaType(produce)); + } + } + else { + for (HttpMessageConverter messageConverter : messageConverters) { + if (messageConverter.canWrite(returnValueClass, null)) { + result.addAll(messageConverter.getSupportedMediaTypes()); + } + } + } + if (result.isEmpty()) { + result.add(MediaType.ALL); + } + return result; + } - MediaType.sortByQualityValue(acceptedMediaTypes); - return acceptedMediaTypes; + private Set getAcceptableMediaTypes(HttpInputMessage inputMessage) { + Set result = new HashSet(inputMessage.getHeaders().getAccept()); + if (result.isEmpty()) { + result.add(MediaType.ALL); + } + return result; + } + + private MediaType getMostSpecificMediaType(MediaType type1, MediaType type2) { + return MediaType.SPECIFICITY_COMPARATOR.compare(type1, type2) < 0 ? type1 : type2; } } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java index f719f2db61..31dd5a2f12 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java @@ -23,9 +23,6 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.springframework.core.MethodParameter; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -34,8 +31,6 @@ import org.springframework.http.HttpOutputMessage; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.http.server.ServletServerHttpRequest; -import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.util.Assert; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -71,8 +66,10 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws IOException, HttpMediaTypeNotSupportedException { + HttpInputMessage inputMessage = createInputMessage(webRequest); Class paramType = getHttpEntityType(parameter); + Object body = readWithMessageConverters(webRequest, parameter, paramType); return new HttpEntity(body, inputMessage.getHeaders()); } @@ -88,7 +85,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro else if (typeArgument instanceof GenericArrayType) { Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType(); if (componentType instanceof Class) { - // Surely, there should be a nicer way to do this + // Surely, there should be a nicer way to determine the array type Object array = Array.newInstance((Class) componentType, 0); return array.getClass(); } @@ -97,17 +94,12 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro throw new IllegalArgumentException( "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized"); } - - @Override - protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) { - HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); - return new ServletServerHttpRequest(servletRequest); - } public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { + mavContainer.setResolveView(false); if (returnValue == null) { @@ -129,7 +121,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro Object body = responseEntity.getBody(); if (body != null) { - writeWithMessageConverters(body, createInputMessage(webRequest), outputMessage); + writeWithMessageConverters(body, returnType, createInputMessage(webRequest), outputMessage); } else { // flush headers to the HttpServletResponse @@ -137,10 +129,4 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro } } - @Override - protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) { - HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse(); - return new ServletServerHttpResponse(servletResponse); - } - } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java index 90658a1e1b..1d55a21472 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java @@ -18,15 +18,11 @@ package org.springframework.web.servlet.mvc.method.annotation.support; import java.io.IOException; import java.util.List; - -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.core.MethodParameter; -import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; @@ -37,8 +33,8 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves method arguments annotated with @{@link RequestBody}. - * Handles return values from methods annotated with @{@link ResponseBody}. + * Resolves method arguments annotated with @{@link RequestBody}. Handles return values from methods annotated with + * {@link ResponseBody}. * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -60,25 +56,20 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws IOException, HttpMediaTypeNotSupportedException { return readWithMessageConverters(webRequest, parameter, parameter.getParameterType()); } - @Override - protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) { - HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); - return new ServletServerHttpRequest(servletRequest); - } - - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, + public void handleReturnValue(Object returnValue, + MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException { mavContainer.setResolveView(false); if (returnValue != null) { - writeWithMessageConverters(webRequest, returnValue); + writeWithMessageConverters(returnValue, returnType, createInputMessage(webRequest), + createOutputMessage(webRequest)); } } @@ -87,5 +78,5 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse(); return new ServletServerHttpResponse(servletResponse); } - + } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java index 3cdf81d026..a19a2b5789 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java @@ -19,7 +19,9 @@ package org.springframework.web.servlet.mvc.method.condition; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; @@ -54,6 +56,18 @@ class MediaTypesRequestCondition getMediaTypes() { + Set result = new LinkedHashSet(); + for (MediaTypeRequestCondition condition : getConditions()) { + result.add(condition.getMediaType()); + } + return result; + } + + /** * @author Arjen Poutsma */ diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java index 843485e36d..d140510cd7 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java @@ -806,13 +806,10 @@ public class ServletHandlerMethodTests { @Test public void responseBodyNoAcceptableMediaType() throws ServletException, IOException { - initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { + initDispatcherServlet(RequestResponseBodyProducesController.class, new BeanDefinitionRegistrar() { public void register(GenericWebApplicationContext wac) { - RootBeanDefinition converterDef = new RootBeanDefinition(StringHttpMessageConverter.class); - converterDef.getPropertyValues().add("supportedMediaTypes", new MediaType("text", "plain")); RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class); StringHttpMessageConverter converter = new StringHttpMessageConverter(); - converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain"))); adapterDef.getPropertyValues().add("messageConverters", converter); wac.registerBeanDefinition("handlerAdapter", adapterDef); } @@ -923,7 +920,7 @@ public class ServletHandlerMethodTests { * See SPR-6877 */ @Test - public void overlappingMesssageConvertersRequestBody() throws ServletException, IOException { + public void overlappingMessageConvertersRequestBody() throws ServletException, IOException { initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { public void register(GenericWebApplicationContext wac) { RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class); @@ -942,7 +939,7 @@ public class ServletHandlerMethodTests { request.addHeader("Accept", "application/json, text/javascript, */*"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); - assertEquals("Invalid response status code", "application/json", response.getHeader("Content-Type")); + assertEquals("Invalid content-type", "application/json", response.getHeader("Content-Type")); } @Test @@ -1006,7 +1003,7 @@ public class ServletHandlerMethodTests { request.setContentType("application/xml"); response = new MockHttpServletResponse(); servlet.service(request, response); - assertEquals(404, response.getStatus()); + assertEquals(415, response.getStatus()); } @Test @@ -1053,6 +1050,12 @@ public class ServletHandlerMethodTests { response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("xml", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "application/msword"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(406, response.getStatus()); } @Test @@ -2159,6 +2162,16 @@ public class ServletHandlerMethodTests { } } + @Controller + public static class RequestResponseBodyProducesController { + + @RequestMapping(value = "/something", method = RequestMethod.PUT, produces = "text/plain") + @ResponseBody + public String handle(@RequestBody String body) throws IOException { + return body; + } + } + @Controller public static class ResponseBodyVoidController { @@ -2608,7 +2621,7 @@ public class ServletHandlerMethodTests { } private interface BeanDefinitionRegistrar { - public void register(GenericWebApplicationContext context); + void register(GenericWebApplicationContext context); } @SuppressWarnings("serial") diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java index c781b65fb7..28c9281931 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java @@ -16,26 +16,14 @@ package org.springframework.web.servlet.mvc.method.annotation.support; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.isA; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.Collections; import org.easymock.Capture; import org.junit.Before; import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -49,9 +37,13 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + /** * Test fixture with {@link HttpEntityMethodProcessor} and mock {@link HttpMessageConverter}. * @@ -70,9 +62,10 @@ public class HttpEntityMethodProcessorTests { private MethodParameter returnTypeResponseEntity; private MethodParameter returnTypeHttpEntity; private MethodParameter returnTypeInt; + private MethodParameter returnTypeResponseEntityProduces; private ModelAndViewContainer mavContainer; - + private ServletWebRequest webRequest; private MockHttpServletResponse servletResponse; @@ -83,10 +76,12 @@ public class HttpEntityMethodProcessorTests { @Before public void setUp() throws Exception { messageConverter = createMock(HttpMessageConverter.class); - - List> messageConverters = new ArrayList>(); - messageConverters.add(messageConverter); - processor = new HttpEntityMethodProcessor(messageConverters); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN)); + replay(messageConverter); + + processor = new HttpEntityMethodProcessor(Collections.>singletonList(messageConverter)); + reset(messageConverter); + Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE); paramHttpEntity = new MethodParameter(handle1, 0); @@ -95,8 +90,11 @@ public class HttpEntityMethodProcessorTests { returnTypeResponseEntity = new MethodParameter(handle1, -1); returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1); + returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1); + returnTypeResponseEntityProduces = new MethodParameter(getClass().getMethod("handle4"), -1); + mavContainer = new ModelAndViewContainer(); servletRequest = new MockHttpServletRequest(); @@ -124,7 +122,6 @@ public class HttpEntityMethodProcessorTests { servletRequest.addHeader("Content-Type", contentType.toString()); String body = "Foo"; - expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType)); expect(messageConverter.canRead(String.class, contentType)).andReturn(true); expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body); replay(messageConverter); @@ -165,6 +162,8 @@ public class HttpEntityMethodProcessorTests { MediaType accepted = MediaType.TEXT_PLAIN; servletRequest.addHeader("Accept", accepted.toString()); + expect(messageConverter.canWrite(String.class, null)).andReturn(true); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN)); expect(messageConverter.canWrite(String.class, accepted)).andReturn(true); messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class)); replay(messageConverter); @@ -175,19 +174,53 @@ public class HttpEntityMethodProcessorTests { verify(messageConverter); } + @Test + public void handleReturnValueProduces() throws Exception { + String body = "Foo"; + ResponseEntity returnValue = new ResponseEntity(body, HttpStatus.OK); + + servletRequest.addHeader("Accept", "text/*"); + + expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true); + messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class)); + replay(messageConverter); + + processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest); + + assertFalse(mavContainer.isResolveView()); + verify(messageConverter); + } + @Test(expected = HttpMediaTypeNotAcceptableException.class) public void handleReturnValueNotAcceptable() throws Exception { String body = "Foo"; ResponseEntity returnValue = new ResponseEntity(body, HttpStatus.OK); + MediaType accepted = MediaType.APPLICATION_ATOM_XML; + servletRequest.addHeader("Accept", accepted.toString()); + + expect(messageConverter.canWrite(String.class, null)).andReturn(true); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN)); + expect(messageConverter.canWrite(String.class, accepted)).andReturn(false); + replay(messageConverter); + + processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); + + fail("Expected exception"); + } + + @Test(expected = HttpMediaTypeNotAcceptableException.class) + public void handleReturnValueNotAcceptableProduces() throws Exception { + String body = "Foo"; + ResponseEntity returnValue = new ResponseEntity(body, HttpStatus.OK); + MediaType accepted = MediaType.TEXT_PLAIN; servletRequest.addHeader("Accept", accepted.toString()); expect(messageConverter.canWrite(String.class, accepted)).andReturn(false); - expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); replay(messageConverter); - processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); + processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest); fail("Expected exception"); } @@ -211,8 +244,10 @@ public class HttpEntityMethodProcessorTests { ResponseEntity returnValue = new ResponseEntity("body", responseHeaders, HttpStatus.ACCEPTED); Capture outputMessage = new Capture(); - expect(messageConverter.canWrite(String.class, MediaType.ALL)).andReturn(true); - messageConverter.write(eq("body"), eq(MediaType.ALL), capture(outputMessage)); + expect(messageConverter.canWrite(String.class, null)).andReturn(true); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN)); + expect(messageConverter.canWrite(String.class, MediaType.TEXT_PLAIN)).andReturn(true); + messageConverter.write(eq("body"), eq(MediaType.TEXT_PLAIN), capture(outputMessage)); replay(messageConverter); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); @@ -234,4 +269,10 @@ public class HttpEntityMethodProcessorTests { return 42; } + @RequestMapping(produces = {"text/html", "application/xhtml+xml"}) + public ResponseEntity handle4() { + return null; + } + + } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java index f050b7f929..6db478b9b5 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java @@ -16,24 +16,13 @@ package org.springframework.web.servlet.mvc.method.annotation.support; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.isA; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.Collections; import org.junit.Before; import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; @@ -44,11 +33,15 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + /** * Test fixture with {@link RequestResponseBodyMethodProcessor} and mock {@link HttpMessageConverter}. * @@ -66,31 +59,40 @@ public class RequestResponseBodyMethodProcessorTests { private MethodParameter returnTypeString; private MethodParameter returnTypeInt; + private MethodParameter returnTypeStringProduces; + private ModelAndViewContainer mavContainer; - + private NativeWebRequest webRequest; private MockHttpServletRequest servletRequest; + private MockHttpServletResponse servletResponse; + @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { messageConverter = createMock(HttpMessageConverter.class); - - List> messageConverters = new ArrayList>(); - messageConverters.add(messageConverter); - processor = new RequestResponseBodyMethodProcessor(messageConverters); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN)); + replay(messageConverter); + + processor = new RequestResponseBodyMethodProcessor(Collections.>singletonList(messageConverter)); + reset(messageConverter); Method handle = getClass().getMethod("handle1", String.class, Integer.TYPE); paramRequestBodyString = new MethodParameter(handle, 0); paramInt = new MethodParameter(handle, 1); returnTypeString = new MethodParameter(handle, -1); + returnTypeInt = new MethodParameter(getClass().getMethod("handle2"), -1); + returnTypeStringProduces = new MethodParameter(getClass().getMethod("handle3"), -1); + mavContainer = new ModelAndViewContainer(); servletRequest = new MockHttpServletRequest(); - webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse()); + servletResponse = new MockHttpServletResponse(); + webRequest = new ServletWebRequest(servletRequest, servletResponse); } @Test @@ -111,7 +113,6 @@ public class RequestResponseBodyMethodProcessorTests { servletRequest.addHeader("Content-Type", contentType.toString()); String body = "Foo"; - expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType)); expect(messageConverter.canRead(String.class, contentType)).andReturn(true); expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body); @@ -150,6 +151,8 @@ public class RequestResponseBodyMethodProcessorTests { servletRequest.addHeader("Accept", accepted.toString()); String body = "Foo"; + expect(messageConverter.canWrite(String.class, null)).andReturn(true); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN)); expect(messageConverter.canWrite(String.class, accepted)).andReturn(true); messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class)); replay(messageConverter); @@ -160,16 +163,47 @@ public class RequestResponseBodyMethodProcessorTests { verify(messageConverter); } + @Test + public void handleReturnValueProduces() throws Exception { + String body = "Foo"; + + servletRequest.addHeader("Accept", "text/*"); + + expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true); + messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class)); + replay(messageConverter); + + processor.handleReturnValue(body, returnTypeStringProduces, mavContainer, webRequest); + + assertFalse(mavContainer.isResolveView()); + verify(messageConverter); + } + + @Test(expected = HttpMediaTypeNotAcceptableException.class) public void handleReturnValueNotAcceptable() throws Exception { + MediaType accepted = MediaType.APPLICATION_ATOM_XML; + servletRequest.addHeader("Accept", accepted.toString()); + + expect(messageConverter.canWrite(String.class, null)).andReturn(true); + expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN)); + expect(messageConverter.canWrite(String.class, accepted)).andReturn(false); + replay(messageConverter); + + processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest); + + fail("Expected exception"); + } + + @Test(expected = HttpMediaTypeNotAcceptableException.class) + public void handleReturnValueNotAcceptableProduces() throws Exception { MediaType accepted = MediaType.TEXT_PLAIN; servletRequest.addHeader("Accept", accepted.toString()); expect(messageConverter.canWrite(String.class, accepted)).andReturn(false); - expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); replay(messageConverter); - processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest); + processor.handleReturnValue("Foo", returnTypeStringProduces, mavContainer, webRequest); fail("Expected exception"); } @@ -182,5 +216,11 @@ public class RequestResponseBodyMethodProcessorTests { public int handle2() { return 42; } - + + @RequestMapping(produces = {"text/html", "application/xhtml+xml"}) + @ResponseBody + public String handle3() { + return null; + } + } \ No newline at end of file diff --git a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java index 352d518dff..fa7886216a 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java +++ b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java @@ -322,7 +322,7 @@ public class MediaType implements Comparable { } /** - * Indicate whether the {@linkplain #getType() type} is the wildcard character * or not. + * Indicates whether the {@linkplain #getType() type} is the wildcard character * or not. */ public boolean isWildcardType() { return WILDCARD_TYPE.equals(type); @@ -336,13 +336,22 @@ public class MediaType implements Comparable { } /** - * Indicate whether the {@linkplain #getSubtype() subtype} is the wildcard character * or not. + * Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard character * or not. * @return whether the subtype is * */ public boolean isWildcardSubtype() { return WILDCARD_TYPE.equals(subtype); } + /** + * Indicates whether this media type is concrete, i.e. whether neither the type or subtype is a wildcard + * character *. + * @return whether this media type is concrete + */ + public boolean isConcrete() { + return !isWildcardType() && !isWildcardSubtype(); + } + /** * Return the character set, as indicated by a charset parameter, if any. * @return the character set; or null if not available diff --git a/org.springframework.web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java b/org.springframework.web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java index de5d672820..051ebd430c 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java +++ b/org.springframework.web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2011 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. @@ -16,6 +16,7 @@ package org.springframework.web; +import java.util.Collection; import javax.servlet.ServletException; /** @@ -49,6 +50,15 @@ public class HttpRequestMethodNotSupportedException extends ServletException { this(method, supportedMethods, "Request method '" + method + "' not supported"); } + /** + * Create a new HttpRequestMethodNotSupportedException. + * @param method the unsupported HTTP request method + * @param supportedMethods the actually supported HTTP methods + */ + public HttpRequestMethodNotSupportedException(String method, Collection supportedMethods) { + this(method, supportedMethods.toArray(new String[supportedMethods.size()])); + } + /** * Create a new HttpRequestMethodNotSupportedException. * @param method the unsupported HTTP request method diff --git a/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java b/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java index ffd07d479f..950b0114b4 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java @@ -16,12 +16,6 @@ package org.springframework.http; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; @@ -30,9 +24,12 @@ import java.util.List; import java.util.Random; import org.junit.Test; + import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; +import static org.junit.Assert.*; + /** * @author Arjen Poutsma * @author Juergen Hoeller @@ -499,5 +496,14 @@ public class MediaTypeTests { MediaType mediaType = MediaType.parseMediaType("application/xml"); assertEquals(mediaType, conversionService.convert("application/xml", MediaType.class)); } + + @Test + public void isConcrete() { + assertTrue("text/plain not concrete", MediaType.TEXT_PLAIN.isConcrete()); + assertFalse("*/* concrete", MediaType.ALL.isConcrete()); + assertFalse("text/* concrete", new MediaType("text", "*").isConcrete()); + } + + }