From 3363d05879537979a13e057f036257a064e1b505 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 21 Jul 2011 16:24:33 +0000 Subject: [PATCH] SPR-8483 Add integration test for accessing multipart request parts with @RequestPart --- org.springframework.web.servlet/.classpath | 2 + org.springframework.web.servlet/ivy.xml | 4 + org.springframework.web.servlet/pom.xml | 12 ++ .../RequestPartMethodArgumentResolver.java | 26 ++- .../http/client/FreePortScanner.java | 99 ++++++++++ .../support/RequestPartIntegrationTests.java | 172 ++++++++++++++++++ ...equestPartMethodArgumentResolverTests.java | 9 +- .../mvc/method/annotation/support/logo.jpg | Bin 0 -> 17031 bytes .../RequestParamMethodArgumentResolver.java | 46 ++--- ...questParamMethodArgumentResolverTests.java | 9 +- 10 files changed, 344 insertions(+), 35 deletions(-) create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/http/client/FreePortScanner.java create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartIntegrationTests.java create mode 100644 org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/mvc/method/annotation/support/logo.jpg diff --git a/org.springframework.web.servlet/.classpath b/org.springframework.web.servlet/.classpath index 227369a8fb..e899ddfdf6 100644 --- a/org.springframework.web.servlet/.classpath +++ b/org.springframework.web.servlet/.classpath @@ -40,6 +40,8 @@ + + diff --git a/org.springframework.web.servlet/ivy.xml b/org.springframework.web.servlet/ivy.xml index 613b79f569..4ec4bfa525 100644 --- a/org.springframework.web.servlet/ivy.xml +++ b/org.springframework.web.servlet/ivy.xml @@ -109,6 +109,10 @@ + + diff --git a/org.springframework.web.servlet/pom.xml b/org.springframework.web.servlet/pom.xml index da6ef572f7..26624b8c03 100644 --- a/org.springframework.web.servlet/pom.xml +++ b/org.springframework.web.servlet/pom.xml @@ -310,5 +310,17 @@ 1.6 test + + org.mortbay.jetty + jetty + 6.1.9 + test + + + servlet-api-2.5 + org.mortbay.jetty + + + diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java index 211cce1364..73c594ab19 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java @@ -31,6 +31,7 @@ import org.springframework.validation.Errors; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -79,23 +80,28 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM /** * Supports the following: *
    - *
  • @RequestPart method arguments. - *
  • Arguments of type {@link MultipartFile} even if not annotated. - *
  • Arguments of type {@code javax.servlet.http.Part} even if not annotated. + *
  • @RequestPart-annotated method arguments. + *
  • Arguments of type {@link MultipartFile} unless annotated with {@link RequestParam}. + *
  • Arguments of type {@code javax.servlet.http.Part} unless annotated with {@link RequestParam}. *
*/ public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(RequestPart.class)) { return true; } - else if (MultipartFile.class.equals(parameter.getParameterType())) { - return true; - } - else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) { - return true; - } else { - return false; + if (parameter.hasParameterAnnotation(RequestParam.class)){ + return false; + } + else if (MultipartFile.class.equals(parameter.getParameterType())) { + return true; + } + else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) { + return true; + } + else { + return false; + } } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/http/client/FreePortScanner.java b/org.springframework.web.servlet/src/test/java/org/springframework/http/client/FreePortScanner.java new file mode 100644 index 0000000000..3591eaca44 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/http/client/FreePortScanner.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http.client; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.util.Random; + +import org.springframework.util.Assert; + +/** + * Utility class that finds free BSD ports for use in testing scenario's. + * + * @author Ben Hale + * @author Arjen Poutsma + */ +public abstract class FreePortScanner { + + private static final int MIN_SAFE_PORT = 1024; + + private static final int MAX_PORT = 65535; + + private static final Random random = new Random(); + + /** + * Returns the number of a free port in the default range. + */ + public static int getFreePort() { + return getFreePort(MIN_SAFE_PORT, MAX_PORT); + } + + /** + * Returns the number of a free port in the given range. + */ + public static int getFreePort(int minPort, int maxPort) { + Assert.isTrue(minPort > 0, "'minPort' must be larger than 0"); + Assert.isTrue(maxPort > minPort, "'maxPort' must be larger than minPort"); + int portRange = maxPort - minPort; + int candidatePort; + int searchCounter = 0; + do { + if (++searchCounter > portRange) { + throw new IllegalStateException( + String.format("There were no ports available in the range %d to %d", minPort, maxPort)); + } + candidatePort = getRandomPort(minPort, portRange); + } + while (!isPortAvailable(candidatePort)); + + return candidatePort; + } + + private static int getRandomPort(int minPort, int portRange) { + return minPort + random.nextInt(portRange); + } + + private static boolean isPortAvailable(int port) { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(); + } + catch (IOException ex) { + throw new IllegalStateException("Unable to create ServerSocket.", ex); + } + + try { + InetSocketAddress sa = new InetSocketAddress(port); + serverSocket.bind(sa); + return true; + } + catch (IOException ex) { + return false; + } + finally { + try { + serverSocket.close(); + } + catch (IOException ex) { + // ignore + } + } + } + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartIntegrationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartIntegrationTests.java new file mode 100644 index 0000000000..3115a97edd --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartIntegrationTests.java @@ -0,0 +1,172 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.mvc.method.annotation.support; + +import static org.junit.Assert.assertEquals; + +import java.net.URI; +import java.util.Arrays; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.FreePortScanner; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.ResourceHttpMessageConverter; +import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; +import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter; +import org.springframework.stereotype.Controller; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartResolver; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +/** + * Test access to parts of a multipart request with {@link RequestPart}. + * + * @author Rossen Stoyanchev + */ +public class RequestPartIntegrationTests { + + private RestTemplate restTemplate; + + private static Server server; + + private static String baseUrl; + + @BeforeClass + public static void startServer() throws Exception { + + int port = FreePortScanner.getFreePort(); + baseUrl = "http://localhost:" + port; + + server = new Server(port); + Context context = new Context(server, "/"); + + ServletHolder commonsResolverServlet = new ServletHolder(DispatcherServlet.class); + commonsResolverServlet.setInitParameter("contextConfigLocation", CommonsMultipartResolverTestConfig.class.getName()); + commonsResolverServlet.setInitParameter("contextClass", AnnotationConfigWebApplicationContext.class.getName()); + context.addServlet(commonsResolverServlet, "/commons/*"); + + server.start(); + } + + @Before + public void setUp() { + XmlAwareFormHttpMessageConverter converter = new XmlAwareFormHttpMessageConverter(); + converter.setPartConverters(Arrays.>asList( + new ResourceHttpMessageConverter(), new MappingJacksonHttpMessageConverter())); + + restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); + restTemplate.setMessageConverters(Arrays.>asList(converter)); + } + + @AfterClass + public static void stopServer() throws Exception { + if (server != null) { + server.stop(); + } + } + + @Test + public void commonsMultipartResolver() throws Exception { + + MultiValueMap parts = new LinkedMultiValueMap(); + HttpEntity jsonEntity = new HttpEntity(new TestData("Jason")); + parts.add("json-data", jsonEntity); + parts.add("file-data", new ClassPathResource("logo.jpg", this.getClass())); + + URI location = restTemplate.postForLocation(baseUrl + "/commons/test", parts); + assertEquals("http://localhost:8080/test/Jason/logo.jpg", location.toString()); + } + + @Configuration + @EnableWebMvc + static class RequestPartTestConfig extends WebMvcConfigurerAdapter { + + @Bean + public RequestPartTestController controller() { + return new RequestPartTestController(); + } + } + + static class CommonsMultipartResolverTestConfig extends RequestPartTestConfig { + + @Bean + public MultipartResolver multipartResolver() { + return new CommonsMultipartResolver(); + } + + } + + @SuppressWarnings("unused") + @Controller + private static class RequestPartTestController { + + @RequestMapping(value = "/test", method = RequestMethod.POST, consumes = { "multipart/mixed", "multipart/form-data" }) + public ResponseEntity create(@RequestPart("json-data") TestData testData, @RequestPart("file-data") MultipartFile file) { + String url = "http://localhost:8080/test/" + testData.getName() + "/" + file.getOriginalFilename(); + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(URI.create(url)); + return new ResponseEntity(headers, HttpStatus.CREATED); + } + } + + @SuppressWarnings("unused") + private static class TestData { + + private String name; + + public TestData() { + super(); + } + + public TestData(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java index 0f91e29620..ecf860f8f3 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java @@ -54,6 +54,7 @@ import org.springframework.mock.web.MockPart; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -84,6 +85,7 @@ public class RequestPartMethodArgumentResolverTests { private MethodParameter paramInt; private MethodParameter paramMultipartFileNotAnnot; private MethodParameter paramServlet30Part; + private MethodParameter paramRequestParamAnnot; private NativeWebRequest webRequest; @@ -96,7 +98,7 @@ public class RequestPartMethodArgumentResolverTests { public void setUp() throws Exception { Method method = getClass().getMethod("handle", SimpleBean.class, SimpleBean.class, SimpleBean.class, - MultipartFile.class, List.class, Integer.TYPE, MultipartFile.class, Part.class); + MultipartFile.class, List.class, Integer.TYPE, MultipartFile.class, Part.class, MultipartFile.class); paramRequestPart = new MethodParameter(method, 0); paramRequestPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer()); @@ -109,6 +111,7 @@ public class RequestPartMethodArgumentResolverTests { paramMultipartFileNotAnnot.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer()); paramServlet30Part = new MethodParameter(method, 7); paramServlet30Part.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer()); + paramRequestParamAnnot = new MethodParameter(method, 8); messageConverter = createMock(HttpMessageConverter.class); expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN)); @@ -132,6 +135,7 @@ public class RequestPartMethodArgumentResolverTests { assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFileNotAnnot)); assertTrue("Part parameter not supported", resolver.supportsParameter(paramServlet30Part)); assertFalse("non-RequestPart parameter supported", resolver.supportsParameter(paramInt)); + assertFalse("@RequestParam args not supported", resolver.supportsParameter(paramRequestParamAnnot)); } @Test @@ -266,7 +270,8 @@ public class RequestPartMethodArgumentResolverTests { @RequestPart("requestPart") List multipartFileList, int i, MultipartFile multipartFileNotAnnot, - Part servlet30Part) { + Part servlet30Part, + @RequestParam MultipartFile requestParamAnnot) { } } diff --git a/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/mvc/method/annotation/support/logo.jpg b/org.springframework.web.servlet/src/test/resources/org/springframework/web/servlet/mvc/method/annotation/support/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a70e6af172588135f7f6d62c01519a638392b4a GIT binary patch literal 17031 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 M`2U*|!3*O508laa3;+NC literal 0 HcmV?d00001 diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolver.java index 110ef0fac1..24f3f90dd9 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolver.java @@ -33,6 +33,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.multipart.MultipartFile; @@ -85,35 +86,38 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod /** * Supports the following: *
    - *
  • @RequestParam method arguments. This excludes the case where a parameter is of type - * {@link Map} and the annotation does not specify a request parameter name. See - * {@link RequestParamMapMethodArgumentResolver} instead for such parameters. - *
  • Arguments of type {@link MultipartFile} even if not annotated. - *
  • Arguments of type {@code javax.servlet.http.Part} even if not annotated. + *
  • @RequestParam-annotated method arguments. + * This excludes {@link Map} parameters where the annotation does not specify a name value. + * See {@link RequestParamMapMethodArgumentResolver} instead for such parameters. + *
  • Arguments of type {@link MultipartFile} unless annotated with {@link RequestPart}. + *
  • Arguments of type {@code javax.servlet.http.Part} unless annotated with {@link RequestPart}. + *
  • In default resolution mode, simple type arguments even if not with @RequestParam. *
- * - *

In default resolution mode, simple type arguments not annotated with @RequestParam are also supported. */ public boolean supportsParameter(MethodParameter parameter) { Class paramType = parameter.getParameterType(); - RequestParam requestParamAnnot = parameter.getParameterAnnotation(RequestParam.class); - if (requestParamAnnot != null) { + if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(paramType)) { - return StringUtils.hasText(requestParamAnnot.value()); + String paramName = parameter.getParameterAnnotation(RequestParam.class).value(); + return StringUtils.hasText(paramName); + } + else { + return true; } - return true; - } - else if (MultipartFile.class.equals(paramType)) { - return true; - } - else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) { - return true; - } - else if (this.useDefaultResolution) { - return BeanUtils.isSimpleProperty(paramType); } else { - return false; + if (parameter.hasParameterAnnotation(RequestPart.class)) { + return false; + } + else if (MultipartFile.class.equals(paramType) || "javax.servlet.http.Part".equals(paramType.getName())) { + return true; + } + else if (this.useDefaultResolution) { + return BeanUtils.isSimpleProperty(paramType); + } + else { + return false; + } } } diff --git a/org.springframework.web/src/test/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolverTests.java b/org.springframework.web/src/test/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolverTests.java index 3e8364ff88..1088450320 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolverTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/method/annotation/support/RequestParamMethodArgumentResolverTests.java @@ -41,6 +41,7 @@ import org.springframework.mock.web.MockMultipartHttpServletRequest; import org.springframework.mock.web.MockPart; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.MultipartFile; @@ -64,6 +65,7 @@ public class RequestParamMethodArgumentResolverTests { private MethodParameter paramMultipartFileNotAnnot; private MethodParameter paramMultipartFileList; private MethodParameter paramServlet30Part; + private MethodParameter paramRequestPartAnnot; private NativeWebRequest webRequest; @@ -76,7 +78,7 @@ public class RequestParamMethodArgumentResolverTests { ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); Method method = getClass().getMethod("params", String.class, String[].class, Map.class, MultipartFile.class, - Map.class, String.class, MultipartFile.class, List.class, Part.class); + Map.class, String.class, MultipartFile.class, List.class, Part.class, MultipartFile.class); paramNamedDefaultValueString = new MethodParameter(method, 0); paramNamedStringArray = new MethodParameter(method, 1); @@ -91,6 +93,7 @@ public class RequestParamMethodArgumentResolverTests { paramMultipartFileList.initParameterNameDiscovery(paramNameDiscoverer); paramServlet30Part = new MethodParameter(method, 8); paramServlet30Part.initParameterNameDiscovery(paramNameDiscoverer); + paramRequestPartAnnot = new MethodParameter(method, 9); request = new MockHttpServletRequest(); webRequest = new ServletWebRequest(request, new MockHttpServletResponse()); @@ -110,6 +113,7 @@ public class RequestParamMethodArgumentResolverTests { resolver = new RequestParamMethodArgumentResolver(null, false); assertFalse(resolver.supportsParameter(paramStringNotAnnot)); + assertFalse(resolver.supportsParameter(paramRequestPartAnnot)); } @Test @@ -225,7 +229,8 @@ public class RequestParamMethodArgumentResolverTests { String stringNotAnnot, MultipartFile multipartFileNotAnnot, List multipartFileList, - Part servlet30Part) { + Part servlet30Part, + @RequestPart MultipartFile requestPartAnnot) { } }