diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 19:55:01 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 19:55:01 +0200 |
commit | 75a721d1019da2a2fa86e24ff439df4a224e5b19 (patch) | |
tree | 2c44c00ce2c8641cccad177177e5682e187a17ea /spring-web/src/test/java/org/springframework/web | |
parent | 9eaca6a06af3cbceb3754de19d477be770614265 (diff) |
Imported Upstream version 4.3.2
Diffstat (limited to 'spring-web/src/test/java/org/springframework/web')
25 files changed, 1208 insertions, 415 deletions
diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index e9d95c2a..baabc764 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -30,10 +30,11 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * Test fixture for {@link ContentNegotiationManagerFactoryBean} tests. + * * @author Rossen Stoyanchev */ public class ContentNegotiationManagerFactoryBeanTests { @@ -119,9 +120,7 @@ public class ContentNegotiationManagerFactoryBeanTests { assertEquals(Collections.emptyList(), manager.resolveMediaTypes(this.webRequest)); } - // SPR-10170 - - @Test(expected = HttpMediaTypeNotAcceptableException.class) + @Test(expected = HttpMediaTypeNotAcceptableException.class) // SPR-10170 public void favorPathWithIgnoreUnknownPathExtensionTurnedOff() throws Exception { this.factoryBean.setFavorPathExtension(true); this.factoryBean.setIgnoreUnknownPathExtensions(false); @@ -152,9 +151,7 @@ public class ContentNegotiationManagerFactoryBeanTests { manager.resolveMediaTypes(this.webRequest)); } - // SPR-10170 - - @Test(expected = HttpMediaTypeNotAcceptableException.class) + @Test(expected = HttpMediaTypeNotAcceptableException.class) // SPR-10170 public void favorParameterWithUnknownMediaType() throws HttpMediaTypeNotAcceptableException { this.factoryBean.setFavorParameter(true); this.factoryBean.afterPropertiesSet(); @@ -188,16 +185,12 @@ public class ContentNegotiationManagerFactoryBeanTests { manager.resolveMediaTypes(this.webRequest)); // SPR-10513 - this.servletRequest.addHeader("Accept", MediaType.ALL_VALUE); - assertEquals(Collections.singletonList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); } - // SPR-12286 - - @Test + @Test // SPR-12286 public void setDefaultContentTypeWithStrategy() throws Exception { this.factoryBean.setDefaultContentTypeStrategy(new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON)); this.factoryBean.afterPropertiesSet(); @@ -216,7 +209,6 @@ public class ContentNegotiationManagerFactoryBeanTests { private final Map<String, String> mimeTypes = new HashMap<>(); - public Map<String, String> getMimeTypes() { return this.mimeTypes; } diff --git a/spring-web/src/test/java/org/springframework/web/accept/HeaderContentNegotiationStrategyTests.java b/spring-web/src/test/java/org/springframework/web/accept/HeaderContentNegotiationStrategyTests.java index 72f8aca9..f6fa0d6b 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/HeaderContentNegotiationStrategyTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/HeaderContentNegotiationStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.accept; import java.util.List; -import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; @@ -32,21 +32,16 @@ import static org.junit.Assert.*; * Test fixture for HeaderContentNegotiationStrategy tests. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ public class HeaderContentNegotiationStrategyTests { - private HeaderContentNegotiationStrategy strategy; + private final HeaderContentNegotiationStrategy strategy = new HeaderContentNegotiationStrategy(); - private NativeWebRequest webRequest; + private final MockHttpServletRequest servletRequest = new MockHttpServletRequest(); - private MockHttpServletRequest servletRequest; + private final NativeWebRequest webRequest = new ServletWebRequest(this.servletRequest); - @Before - public void setup() { - this.strategy = new HeaderContentNegotiationStrategy(); - this.servletRequest = new MockHttpServletRequest(); - this.webRequest = new ServletWebRequest(servletRequest ); - } @Test public void resolveMediaTypes() throws Exception { @@ -60,7 +55,20 @@ public class HeaderContentNegotiationStrategyTests { assertEquals("text/plain;q=0.5", mediaTypes.get(3).toString()); } - @Test(expected=HttpMediaTypeNotAcceptableException.class) + @Test // SPR-14506 + public void resolveMediaTypesFromMultipleHeaderValues() throws Exception { + this.servletRequest.addHeader("Accept", "text/plain; q=0.5, text/html"); + this.servletRequest.addHeader("Accept", "text/x-dvi; q=0.8, text/x-c"); + List<MediaType> mediaTypes = this.strategy.resolveMediaTypes(this.webRequest); + + assertEquals(4, mediaTypes.size()); + assertEquals("text/html", mediaTypes.get(0).toString()); + assertEquals("text/x-c", mediaTypes.get(1).toString()); + assertEquals("text/x-dvi;q=0.8", mediaTypes.get(2).toString()); + assertEquals("text/plain;q=0.5", mediaTypes.get(3).toString()); + } + + @Test(expected = HttpMediaTypeNotAcceptableException.class) public void resolveMediaTypesParseError() throws Exception { this.servletRequest.addHeader("Accept", "textplain; q=0.5"); this.strategy.resolveMediaTypes(this.webRequest); diff --git a/spring-web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java b/spring-web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java index 32b4c0e1..508be6d3 100644 --- a/spring-web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java +++ b/spring-web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,10 +16,15 @@ package org.springframework.web.bind.support; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + import java.beans.PropertyEditorSupport; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.Test; @@ -34,8 +39,6 @@ import org.springframework.web.bind.ServletRequestParameterPropertyValues; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.support.StringMultipartFileEditor; -import static org.junit.Assert.*; - /** * @author Juergen Hoeller */ @@ -126,6 +129,31 @@ public class WebRequestDataBinderTests { assertFalse(target.isPostProcessed()); } + // SPR-13502 + @Test + public void testCollectionFieldsDefault() throws Exception { + TestBean target = new TestBean(); + target.setSomeSet(null); + target.setSomeList(null); + target.setSomeMap(null); + WebRequestDataBinder binder = new WebRequestDataBinder(target); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addParameter("_someSet", "visible"); + request.addParameter("_someList", "visible"); + request.addParameter("_someMap", "visible"); + + binder.bind(new ServletWebRequest(request)); + assertThat(target.getSomeSet(), notNullValue()); + assertThat(target.getSomeSet(), isA(Set.class)); + + assertThat(target.getSomeList(), notNullValue()); + assertThat(target.getSomeList(), isA(List.class)); + + assertThat(target.getSomeMap(), notNullValue()); + assertThat(target.getSomeMap(), isA(Map.class)); + } + @Test public void testFieldDefaultPreemptsFieldMarker() throws Exception { TestBean target = new TestBean(); diff --git a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java index 6210cec1..258a9cc4 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,8 +16,11 @@ package org.springframework.web.client; +import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.util.Collections; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -25,6 +28,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.junit.Assert; import org.junit.Test; import org.springframework.core.io.ClassPathResource; @@ -32,16 +36,28 @@ import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.AsyncClientHttpRequestExecution; +import org.springframework.http.client.AsyncClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory; +import org.springframework.http.client.support.HttpRequestWrapper; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Arjen Poutsma @@ -49,14 +65,14 @@ import static org.junit.Assert.*; */ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCase { - private final AsyncRestTemplate template = new AsyncRestTemplate(new HttpComponentsAsyncClientHttpRequestFactory()); + private final AsyncRestTemplate template = new AsyncRestTemplate( + new HttpComponentsAsyncClientHttpRequestFactory()); @Test public void getEntity() throws Exception { - Future<ResponseEntity<String>> futureEntity = - template.getForEntity(baseUrl + "/{method}", String.class, "get"); - ResponseEntity<String> entity = futureEntity.get(); + Future<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/{method}", String.class, "get"); + ResponseEntity<String> entity = future.get(); assertEquals("Invalid content", helloWorld, entity.getBody()); assertFalse("No headers", entity.getHeaders().isEmpty()); assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType()); @@ -65,10 +81,9 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa @Test public void multipleFutureGets() throws Exception { - Future<ResponseEntity<String>> futureEntity = - template.getForEntity(baseUrl + "/{method}", String.class, "get"); - futureEntity.get(); - futureEntity.get(); + Future<ResponseEntity<String>> future = template.getForEntity(baseUrl + "/{method}", String.class, "get"); + future.get(); + future.get(); } @Test @@ -88,9 +103,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - // wait till done - while (!futureEntity.isDone()) { - } + waitTillDone(futureEntity); } @Test @@ -103,9 +116,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType()); assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode()); }, ex -> fail(ex.getMessage())); - // wait till done - while (!futureEntity.isDone()) { - } + waitTillDone(futureEntity); } @Test @@ -160,8 +171,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!headersFuture.isDone()) { - } + waitTillDone(headersFuture); } @Test @@ -169,15 +179,14 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa ListenableFuture<HttpHeaders> headersFuture = template.headForHeaders(baseUrl + "/get"); headersFuture.addCallback(result -> assertTrue("No Content-Type header", result.containsKey("Content-Type")), ex -> fail(ex.getMessage())); - while (!headersFuture.isDone()) { - } + waitTillDone(headersFuture); } @Test public void postForLocation() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); - HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); + HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders); Future<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); URI location = locationFuture.get(); assertEquals("Invalid location", new URI(baseUrl + "/post/1"), location); @@ -187,7 +196,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa public void postForLocationCallback() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); - HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); + HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders); final URI expected = new URI(baseUrl + "/post/1"); ListenableFuture<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); locationFuture.addCallback(new ListenableFutureCallback<URI>() { @@ -200,21 +209,19 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!locationFuture.isDone()) { - } + waitTillDone(locationFuture); } @Test public void postForLocationCallbackWithLambdas() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); - HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); + HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders); final URI expected = new URI(baseUrl + "/post/1"); ListenableFuture<URI> locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); locationFuture.addCallback(result -> assertEquals("Invalid location", expected, result), ex -> fail(ex.getMessage())); - while (!locationFuture.isDone()) { - } + waitTillDone(locationFuture); } @Test @@ -241,8 +248,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!responseEntityFuture.isDone()) { - } + waitTillDone(responseEntityFuture); } @Test @@ -250,10 +256,10 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld); ListenableFuture<ResponseEntity<String>> responseEntityFuture = template.postForEntity(baseUrl + "/{method}", requestEntity, String.class, "post"); - responseEntityFuture.addCallback(result -> assertEquals("Invalid content", helloWorld, result.getBody()), + responseEntityFuture.addCallback( + result -> assertEquals("Invalid content", helloWorld, result.getBody()), ex -> fail(ex.getMessage())); - while (!responseEntityFuture.isDone()) { - } + waitTillDone(responseEntityFuture); } @Test @@ -277,8 +283,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!responseEntityFuture.isDone()) { - } + waitTillDone(responseEntityFuture); } @Test @@ -300,16 +305,14 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!deletedFuture.isDone()) { - } + waitTillDone(deletedFuture); } @Test public void deleteCallbackWithLambdas() throws Exception { ListenableFuture<?> deletedFuture = template.delete(new URI(baseUrl + "/delete")); - deletedFuture.addCallback(result -> assertNull(result), ex -> fail(ex.getMessage())); - while (!deletedFuture.isDone()) { - } + deletedFuture.addCallback(Assert::assertNull, ex -> fail(ex.getMessage())); + waitTillDone(deletedFuture); } @Test @@ -377,8 +380,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertNotNull(ex.getResponseBodyAsString()); } }); - while (!future.isDone()) { - } + waitTillDone(future); } @Test @@ -391,8 +393,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertNotNull(hcex.getStatusText()); assertNotNull(hcex.getResponseBodyAsString()); }); - while (!future.isDone()) { - } + waitTillDone(future); } @Test @@ -429,8 +430,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertNotNull(hsex.getResponseBodyAsString()); } }); - while (!future.isDone()) { - } + waitTillDone(future); } @Test @@ -443,8 +443,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertNotNull(hsex.getStatusText()); assertNotNull(hsex.getResponseBodyAsString()); }); - while (!future.isDone()) { - } + waitTillDone(future); } @Test @@ -469,8 +468,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!allowedFuture.isDone()) { - } + waitTillDone(allowedFuture); } @Test @@ -479,8 +477,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa allowedFuture.addCallback(result -> assertEquals("Invalid response", EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD,HttpMethod.TRACE), result), ex -> fail(ex.getMessage())); - while (!allowedFuture.isDone()) { - } + waitTillDone(allowedFuture); } @Test @@ -513,8 +510,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!responseFuture.isDone()) { - } + waitTillDone(responseFuture); } @Test @@ -527,8 +523,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get"); responseFuture.addCallback(result -> assertEquals("Invalid content", helloWorld, result.getBody()), ex -> fail(ex.getMessage())); - while (!responseFuture.isDone()) { - } + waitTillDone(responseFuture); } @Test @@ -536,7 +531,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.set("MyHeader", "MyValue"); requestHeaders.setContentType(MediaType.TEXT_PLAIN); - HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders); + HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders); Future<ResponseEntity<Void>> resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post"); ResponseEntity<Void> result = resultFuture.get(); @@ -550,7 +545,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.set("MyHeader", "MyValue"); requestHeaders.setContentType(MediaType.TEXT_PLAIN); - HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders); + HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders); ListenableFuture<ResponseEntity<Void>> resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post"); final URI expected =new URI(baseUrl + "/post/1"); @@ -565,8 +560,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa fail(ex.getMessage()); } }); - while (!resultFuture.isDone()) { - } + waitTillDone(resultFuture); } @Test @@ -574,7 +568,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.set("MyHeader", "MyValue"); requestHeaders.setContentType(MediaType.TEXT_PLAIN); - HttpEntity<String> requestEntity = new HttpEntity<String>(helloWorld, requestHeaders); + HttpEntity<String> requestEntity = new HttpEntity<>(helloWorld, requestHeaders); ListenableFuture<ResponseEntity<Void>> resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, Void.class, "post"); final URI expected =new URI(baseUrl + "/post/1"); @@ -582,13 +576,12 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa assertEquals("Invalid location", expected, result.getHeaders().getLocation()); assertFalse(result.hasBody()); }, ex -> fail(ex.getMessage())); - while (!resultFuture.isDone()) { - } + waitTillDone(resultFuture); } @Test public void multipart() throws Exception { - MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>(); + MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); parts.add("name 1", "value 1"); parts.add("name 2", "value 2+1"); parts.add("name 2", "value 2+2"); @@ -600,4 +593,73 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa future.get(); } + @Test + public void getAndInterceptResponse() throws Exception { + RequestInterceptor interceptor = new RequestInterceptor(); + template.setInterceptors(Collections.singletonList(interceptor)); + ListenableFuture<ResponseEntity<String>> future = template.getForEntity("/get", String.class); + + interceptor.latch.await(5, TimeUnit.SECONDS); + assertNotNull(interceptor.response); + assertEquals(HttpStatus.OK, interceptor.response.getStatusCode()); + assertNull(interceptor.exception); + assertEquals(helloWorld, future.get().getBody()); + } + + @Test + public void getAndInterceptError() throws Exception { + RequestInterceptor interceptor = new RequestInterceptor(); + template.setInterceptors(Collections.singletonList(interceptor)); + template.getForEntity("/status/notfound", String.class); + + interceptor.latch.await(5, TimeUnit.SECONDS); + assertNotNull(interceptor.response); + assertEquals(HttpStatus.NOT_FOUND, interceptor.response.getStatusCode()); + assertNull(interceptor.exception); + } + + private void waitTillDone(ListenableFuture<?> future) { + while (!future.isDone()) { + } + } + + + private static class RequestInterceptor implements AsyncClientHttpRequestInterceptor { + + private final CountDownLatch latch = new CountDownLatch(1); + + private volatile ClientHttpResponse response; + + private volatile Throwable exception; + + @Override + public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, + AsyncClientHttpRequestExecution execution) throws IOException { + + request = new HttpRequestWrapper(request) { + + @Override + public URI getURI() { + try { + return new URI(baseUrl + super.getURI().toString()); + } + catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } + }; + + ListenableFuture<ClientHttpResponse> future = execution.executeAsync(request, body); + future.addCallback( + resp -> { + response = resp; + this.latch.countDown(); + }, + ex -> { + exception = ex; + this.latch.countDown(); + }); + return future; + } + } } diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index 149340b6..d5f00fc2 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -20,11 +20,16 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; import org.junit.Test; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; @@ -32,6 +37,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.json.MappingJacksonValue; @@ -215,7 +221,7 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase { bean.setWith2("with"); bean.setWithout("without"); HttpEntity<MySampleBean> entity = new HttpEntity<MySampleBean>(bean, entityHeaders); - String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class, "post"); + String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class); assertTrue(s.contains("\"with1\":\"with\"")); assertTrue(s.contains("\"with2\":\"with\"")); assertTrue(s.contains("\"without\":\"without\"")); @@ -229,7 +235,7 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase { MappingJacksonValue jacksonValue = new MappingJacksonValue(bean); jacksonValue.setSerializationView(MyJacksonView1.class); HttpEntity<MappingJacksonValue> entity = new HttpEntity<MappingJacksonValue>(jacksonValue, entityHeaders); - String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class, "post"); + String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class); assertTrue(s.contains("\"with1\":\"with\"")); assertFalse(s.contains("\"with2\":\"with\"")); assertFalse(s.contains("\"without\":\"without\"")); @@ -243,6 +249,21 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase { assertEquals("Invalid content", helloWorld, s); } + @Test // SPR-13154 + public void jsonPostForObjectWithJacksonTypeInfoList() throws URISyntaxException { + List<ParentClass> list = new ArrayList<>(); + list.add(new Foo("foo")); + list.add(new Bar("bar")); + ParameterizedTypeReference<?> typeReference = new ParameterizedTypeReference<List<ParentClass>>() {}; + RequestEntity<List<ParentClass>> entity = RequestEntity + .post(new URI(baseUrl + "/jsonpost")) + .contentType(new MediaType("application", "json", Charset.forName("UTF-8"))) + .body(list, typeReference.getType()); + String content = template.exchange(entity, String.class).getBody(); + assertTrue(content.contains("\"type\":\"foo\"")); + assertTrue(content.contains("\"type\":\"bar\"")); + } + public interface MyJacksonView1 {}; public interface MyJacksonView2 {}; @@ -290,4 +311,47 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase { } } + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") + public static class ParentClass { + + private String parentProperty; + + public ParentClass() { + } + + public ParentClass(String parentProperty) { + this.parentProperty = parentProperty; + } + + public String getParentProperty() { + return parentProperty; + } + + public void setParentProperty(String parentProperty) { + this.parentProperty = parentProperty; + } + } + + @JsonTypeName("foo") + public static class Foo extends ParentClass { + + public Foo() { + } + + public Foo(String parentProperty) { + super(parentProperty); + } + } + + @JsonTypeName("bar") + public static class Bar extends ParentClass { + + public Bar() { + } + + public Bar(String parentProperty) { + super(parentProperty); + } + } + } diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java index 1ecfc48e..5674ad55 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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 + * 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, @@ -49,6 +49,7 @@ import static org.mockito.BDDMockito.*; /** * @author Arjen Poutsma + * @author Rossen Stoyanchev */ @SuppressWarnings("unchecked") public class RestTemplateTests { @@ -135,7 +136,7 @@ public class RestTemplateTests { given(response.getStatusCode()).willReturn(status); given(response.getStatusText()).willReturn(status.getReasonPhrase()); - Map<String, String> vars = new HashMap<String, String>(2); + Map<String, String> vars = new HashMap<>(2); vars.put("first", null); vars.put("last", "foo"); template.execute("http://example.com/{first}-{last}", HttpMethod.GET, null, null, vars); @@ -278,7 +279,7 @@ public class RestTemplateTests { given(response.getHeaders()).willReturn(new HttpHeaders()); given(response.getBody()).willReturn(null); - Map<String, String> uriVariables = new HashMap<String, String>(2); + Map<String, String> uriVariables = new HashMap<>(2); uriVariables.put("hotel", "1"); uriVariables.put("publicpath", "pics/logo.png"); uriVariables.put("scale", "150x150"); @@ -351,7 +352,7 @@ public class RestTemplateTests { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.setContentType(contentType); - HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); + HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders); URI result = template.postForLocation("http://example.com", entity); assertEquals("Invalid POST result", expected, result); @@ -379,7 +380,7 @@ public class RestTemplateTests { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.set("MyHeader", "MyValue"); - HttpEntity<String> entity = new HttpEntity<String>(helloWorld, entityHeaders); + HttpEntity<String> entity = new HttpEntity<>(helloWorld, entityHeaders); URI result = template.postForLocation("http://example.com", entity); assertEquals("Invalid POST result", expected, result); @@ -622,21 +623,27 @@ public class RestTemplateTests { verify(response).close(); } + // Issue: SPR-9325, SPR-13860 + @Test public void ioException() throws Exception { + String url = "http://example.com/resource?access_token=123"; + given(converter.canRead(String.class, null)).willReturn(true); MediaType mediaType = new MediaType("foo", "bar"); given(converter.getSupportedMediaTypes()).willReturn(Collections.singletonList(mediaType)); - given(requestFactory.createRequest(new URI("http://example.com/resource"), HttpMethod.GET)).willReturn(request); + given(requestFactory.createRequest(new URI(url), HttpMethod.GET)).willReturn(request); given(request.getHeaders()).willReturn(new HttpHeaders()); - given(request.execute()).willThrow(new IOException()); + given(request.execute()).willThrow(new IOException("Socket failure")); try { - template.getForObject("http://example.com/resource", String.class); + template.getForObject(url, String.class); fail("RestClientException expected"); } catch (ResourceAccessException ex) { - // expected + assertEquals("I/O error on GET request for \"http://example.com/resource\": " + + "Socket failure; nested exception is java.io.IOException: Socket failure", + ex.getMessage()); } } @@ -669,7 +676,7 @@ public class RestTemplateTests { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.set("MyHeader", "MyValue"); - HttpEntity<String> requestEntity = new HttpEntity<String>(body, entityHeaders); + HttpEntity<String> requestEntity = new HttpEntity<>(body, entityHeaders); ResponseEntity<Integer> result = template.exchange("http://example.com", HttpMethod.POST, requestEntity, Integer.class); assertEquals("Invalid POST result", expected, result.getBody()); assertEquals("Invalid Content-Type", MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); @@ -692,9 +699,9 @@ public class RestTemplateTests { given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).willReturn(this.request); HttpHeaders requestHeaders = new HttpHeaders(); given(this.request.getHeaders()).willReturn(requestHeaders); - given(converter.canWrite(String.class, null)).willReturn(true); + given(converter.canWrite(String.class, String.class, null)).willReturn(true); String requestBody = "Hello World"; - converter.write(requestBody, null, this.request); + converter.write(requestBody, String.class, null, this.request); given(this.request.execute()).willReturn(response); given(errorHandler.hasError(response)).willReturn(false); List<Integer> expected = Collections.singletonList(42); @@ -703,7 +710,7 @@ public class RestTemplateTests { responseHeaders.setContentLength(10); given(response.getStatusCode()).willReturn(HttpStatus.OK); given(response.getHeaders()).willReturn(responseHeaders); - given(response.getBody()).willReturn(new ByteArrayInputStream(new Integer(42).toString().getBytes())); + given(response.getBody()).willReturn(new ByteArrayInputStream(Integer.toString(42).getBytes())); given(converter.canRead(intList.getType(), null, MediaType.TEXT_PLAIN)).willReturn(true); given(converter.read(eq(intList.getType()), eq(null), any(HttpInputMessage.class))).willReturn(expected); given(response.getStatusCode()).willReturn(HttpStatus.OK); @@ -713,7 +720,7 @@ public class RestTemplateTests { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.set("MyHeader", "MyValue"); - HttpEntity<String> requestEntity = new HttpEntity<String>(requestBody, entityHeaders); + HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, entityHeaders); ResponseEntity<List<Integer>> result = template.exchange("http://example.com", HttpMethod.POST, requestEntity, intList); assertEquals("Invalid POST result", expected, result.getBody()); assertEquals("Invalid Content-Type", MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); diff --git a/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java b/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java index ef1afaad..603e2642 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -78,8 +78,7 @@ public class ServletRequestAttributesTests { request.setSession(session); ServletRequestAttributes attrs = new ServletRequestAttributes(request); attrs.setAttribute(KEY, VALUE, RequestAttributes.SCOPE_SESSION); - Object value = session.getAttribute(KEY); - assertSame(VALUE, value); + assertSame(VALUE, session.getAttribute(KEY)); } @Test @@ -89,11 +88,11 @@ public class ServletRequestAttributesTests { MockHttpServletRequest request = new MockHttpServletRequest(); request.setSession(session); ServletRequestAttributes attrs = new ServletRequestAttributes(request); + assertSame(VALUE, attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION)); attrs.requestCompleted(); request.close(); attrs.setAttribute(KEY, VALUE, RequestAttributes.SCOPE_SESSION); - Object value = session.getAttribute(KEY); - assertSame(VALUE, value); + assertSame(VALUE, session.getAttribute(KEY)); } @Test @@ -104,8 +103,7 @@ public class ServletRequestAttributesTests { request.setSession(session); ServletRequestAttributes attrs = new ServletRequestAttributes(request); attrs.setAttribute(KEY, VALUE, RequestAttributes.SCOPE_GLOBAL_SESSION); - Object value = session.getAttribute(KEY); - assertSame(VALUE, value); + assertSame(VALUE, session.getAttribute(KEY)); } @Test @@ -115,11 +113,11 @@ public class ServletRequestAttributesTests { MockHttpServletRequest request = new MockHttpServletRequest(); request.setSession(session); ServletRequestAttributes attrs = new ServletRequestAttributes(request); + assertSame(VALUE, attrs.getAttribute(KEY, RequestAttributes.SCOPE_GLOBAL_SESSION)); attrs.requestCompleted(); request.close(); attrs.setAttribute(KEY, VALUE, RequestAttributes.SCOPE_GLOBAL_SESSION); - Object value = session.getAttribute(KEY); - assertSame(VALUE, value); + assertSame(VALUE, session.getAttribute(KEY)); } @Test diff --git a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java index 3832ae3a..a5a8b07c 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,8 @@ package org.springframework.web.context.request; +import static org.junit.Assert.*; + import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; @@ -32,8 +34,6 @@ import org.junit.runners.Parameterized.Parameters; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; -import static org.junit.Assert.*; - /** * Parameterized tests for ServletWebRequest * @author Juergen Hoeller @@ -145,6 +145,18 @@ public class ServletWebRequestHttpMethodsTests { } @Test + public void checkNotModifiedETagWithSeparatorChars() { + String eTag = "\"Foo, Bar\""; + servletRequest.addHeader("If-None-Match", eTag); + + assertTrue(request.checkNotModified(eTag)); + + assertEquals(304, servletResponse.getStatus()); + assertEquals(eTag, servletResponse.getHeader("ETag")); + } + + + @Test public void checkModifiedETag() { String currentETag = "\"Foo\""; String oldEtag = "Bar"; @@ -204,6 +216,7 @@ public class ServletWebRequestHttpMethodsTests { assertEquals(dateFormat.format(currentDate.getTime()), servletResponse.getHeader("Last-Modified")); } + // SPR-14224 @Test public void checkNotModifiedETagAndModifiedTimestamp() { String eTag = "\"Foo\""; @@ -212,9 +225,9 @@ public class ServletWebRequestHttpMethodsTests { long oneMinuteAgo = currentEpoch - (1000 * 60); servletRequest.addHeader("If-Modified-Since", oneMinuteAgo); - assertFalse(request.checkNotModified(eTag, currentEpoch)); + assertTrue(request.checkNotModified(eTag, currentEpoch)); - assertEquals(200, servletResponse.getStatus()); + assertEquals(304, servletResponse.getStatus()); assertEquals(eTag, servletResponse.getHeader("ETag")); assertEquals(dateFormat.format(currentEpoch), servletResponse.getHeader("Last-Modified")); } @@ -293,4 +306,28 @@ public class ServletWebRequestHttpMethodsTests { assertEquals(dateFormat.format(epochTime), servletResponse.getHeader("Last-Modified")); } + @Test + public void checkNotModifiedTimestampConditionalPut() throws Exception { + long currentEpoch = currentDate.getTime(); + long oneMinuteAgo = currentEpoch - (1000 * 60); + servletRequest.setMethod("PUT"); + servletRequest.addHeader("If-UnModified-Since", currentEpoch); + + assertFalse(request.checkNotModified(oneMinuteAgo)); + assertEquals(200, servletResponse.getStatus()); + assertEquals(null, servletResponse.getHeader("Last-Modified")); + } + + @Test + public void checkNotModifiedTimestampConditionalPutConflict() throws Exception { + long currentEpoch = currentDate.getTime(); + long oneMinuteAgo = currentEpoch - (1000 * 60); + servletRequest.setMethod("PUT"); + servletRequest.addHeader("If-UnModified-Since", oneMinuteAgo); + + assertTrue(request.checkNotModified(currentEpoch)); + assertEquals(412, servletResponse.getStatus()); + assertEquals(null, servletResponse.getHeader("Last-Modified")); + } + } diff --git a/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java b/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java index ab198238..a44318fc 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java @@ -172,6 +172,11 @@ public class SessionScopeTests { @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { } + + @Override + public boolean requiresDestruction(Object bean) { + return true; + } } @@ -195,6 +200,11 @@ public class SessionScopeTests { ((BeanNameAware) bean).setBeanName(null); } } + + @Override + public boolean requiresDestruction(Object bean) { + return true; + } } } diff --git a/spring-web/src/test/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequestTests.java b/spring-web/src/test/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequestTests.java index 50d84230..2596796e 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequestTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequestTests.java @@ -147,7 +147,7 @@ public class StandardServletAsyncWebRequestTests { } // SPR-13292 - + @Test public void onCompletionHandlerAfterOnErrorEvent() throws Exception { Runnable handler = mock(Runnable.class); diff --git a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java index c763eeec..d4a2ad47 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java @@ -150,8 +150,9 @@ public class WebAsyncManagerTests { try { this.asyncManager.startCallableProcessing(task); fail("Expected Exception"); - }catch(Exception e) { - assertEquals(exception, e); + } + catch (Exception ex) { + assertEquals(exception, ex); } assertFalse(this.asyncManager.hasConcurrentResult()); @@ -162,7 +163,6 @@ public class WebAsyncManagerTests { @Test public void startCallableProcessingPreProcessException() throws Exception { - Callable<Object> task = new StubCallable(21); Exception exception = new Exception(); @@ -183,7 +183,6 @@ public class WebAsyncManagerTests { @Test public void startCallableProcessingPostProcessException() throws Exception { - Callable<Object> task = new StubCallable(21); Exception exception = new Exception(); @@ -205,7 +204,6 @@ public class WebAsyncManagerTests { @Test public void startCallableProcessingPostProcessContinueAfterException() throws Exception { - Callable<Object> task = new StubCallable(21); Exception exception = new Exception(); @@ -231,7 +229,6 @@ public class WebAsyncManagerTests { @Test public void startCallableProcessingWithAsyncTask() throws Exception { - AsyncTaskExecutor executor = mock(AsyncTaskExecutor.class); given(this.asyncWebRequest.getNativeRequest(HttpServletRequest.class)).willReturn(this.servletRequest); @@ -259,7 +256,6 @@ public class WebAsyncManagerTests { @Test public void startDeferredResultProcessing() throws Exception { - DeferredResult<String> deferredResult = new DeferredResult<String>(1000L); String concurrentResult = "abc"; @@ -282,7 +278,6 @@ public class WebAsyncManagerTests { @Test public void startDeferredResultProcessingBeforeConcurrentHandlingException() throws Exception { - DeferredResult<Integer> deferredResult = new DeferredResult<Integer>(); Exception exception = new Exception(); @@ -295,7 +290,7 @@ public class WebAsyncManagerTests { this.asyncManager.startDeferredResultProcessing(deferredResult); fail("Expected Exception"); } - catch(Exception success) { + catch (Exception success) { assertEquals(exception, success); } @@ -328,7 +323,6 @@ public class WebAsyncManagerTests { @Test public void startDeferredResultProcessingPostProcessException() throws Exception { - DeferredResult<Integer> deferredResult = new DeferredResult<Integer>(); Exception exception = new Exception(); @@ -371,6 +365,7 @@ public class WebAsyncManagerTests { verify(this.asyncWebRequest).dispatch(); } + private final class StubCallable implements Callable<Object> { private Object value; @@ -388,6 +383,7 @@ public class WebAsyncManagerTests { } } + @SuppressWarnings("serial") private static class SyncTaskExecutor extends SimpleAsyncTaskExecutor { diff --git a/spring-web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java b/spring-web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java index a712e525..dd56ff00 100644 --- a/spring-web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java +++ b/spring-web/src/test/java/org/springframework/web/context/support/Spr8510Tests.java @@ -49,7 +49,8 @@ public class Spr8510Tests { try { cll.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (Throwable t) { + } + catch (Throwable t) { // assert that an attempt was made to load the correct XML assertTrue(t.getMessage(), t.getMessage().endsWith( "Could not open ServletContext resource [/programmatic.xml]")); @@ -75,7 +76,8 @@ public class Spr8510Tests { try { cll.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (Throwable t) { + } + catch (Throwable t) { // assert that an attempt was made to load the correct XML assertTrue(t.getMessage(), t.getMessage().endsWith( "Could not open ServletContext resource [/from-init-param.xml]")); @@ -98,7 +100,8 @@ public class Spr8510Tests { try { cll.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (Throwable t) { + } + catch (Throwable t) { // assert that an attempt was made to load the correct XML assertTrue(t.getMessage().endsWith( "Could not open ServletContext resource [/from-init-param.xml]")); @@ -125,7 +128,8 @@ public class Spr8510Tests { try { cll.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (Throwable t) { + } + catch (Throwable t) { // assert that an attempt was made to load the correct XML System.out.println(t.getMessage()); assertTrue(t.getMessage().endsWith( @@ -150,7 +154,8 @@ public class Spr8510Tests { try { cll.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); - } catch (Throwable t) { + } + catch (Throwable t) { // assert that an attempt was made to load the correct XML System.out.println(t.getMessage()); assertTrue(t.getMessage().endsWith( diff --git a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java index 8d3651c5..32f68af3 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -176,7 +176,7 @@ public class CorsConfigurationTests { @Test public void checkMethodAllowed() { - assertEquals(Arrays.asList(HttpMethod.GET), config.checkHttpMethod(HttpMethod.GET)); + assertEquals(Arrays.asList(HttpMethod.GET, HttpMethod.HEAD), config.checkHttpMethod(HttpMethod.GET)); config.addAllowedMethod("GET"); assertEquals(Arrays.asList(HttpMethod.GET), config.checkHttpMethod(HttpMethod.GET)); config.addAllowedMethod("POST"); @@ -189,7 +189,7 @@ public class CorsConfigurationTests { assertNull(config.checkHttpMethod(null)); assertNull(config.checkHttpMethod(HttpMethod.DELETE)); config.setAllowedMethods(new ArrayList<>()); - assertNull(config.checkHttpMethod(HttpMethod.HEAD)); + assertNull(config.checkHttpMethod(HttpMethod.POST)); } @Test diff --git a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java index 56ab6166..30a93e30 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -171,7 +171,7 @@ public class DefaultCorsProcessorTests { this.conf.addAllowedOrigin("*"); this.processor.processRequest(this.conf, request, response); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("GET", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); + assertEquals("GET,HEAD", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); } @Test diff --git a/spring-web/src/test/java/org/springframework/web/filter/CharacterEncodingFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/CharacterEncodingFilterTests.java index 64392f91..5f0006c9 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/CharacterEncodingFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/CharacterEncodingFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -145,4 +145,26 @@ public class CharacterEncodingFilterTests { verify(filterChain).doFilter(request, response); } + // SPR-14240 + @Test + public void setForceEncodingOnRequestOnly() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + request.setCharacterEncoding(ENCODING); + given(request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE)).willReturn(null); + given(request.getAttribute(FILTER_NAME + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX)).willReturn(null); + + HttpServletResponse response = mock(HttpServletResponse.class); + FilterChain filterChain = mock(FilterChain.class); + + CharacterEncodingFilter filter = new CharacterEncodingFilter(ENCODING, true, false); + filter.init(new MockFilterConfig(FILTER_NAME)); + filter.doFilter(request, response, filterChain); + + verify(request).setAttribute(FILTER_NAME + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX, Boolean.TRUE); + verify(request).removeAttribute(FILTER_NAME + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX); + verify(request, times(2)).setCharacterEncoding(ENCODING); + verify(response, never()).setCharacterEncoding(ENCODING); + verify(filterChain).doFilter(request, response); + } + } diff --git a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java new file mode 100644 index 00000000..56d93f8c --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java @@ -0,0 +1,241 @@ +/* + * Copyright 2002-2016 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.filter; + +import java.io.IOException; +import java.util.Enumeration; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.mock.web.test.MockFilterChain; +import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for {@link ForwardedHeaderFilter}. + * @author Rossen Stoyanchev + * @author Eddú Meléndez + */ +public class ForwardedHeaderFilterTests { + + private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; // SPR-14372 (case insensitive) + private static final String X_FORWARDED_HOST = "x-forwarded-host"; + private static final String X_FORWARDED_PORT = "x-forwarded-port"; + private static final String X_FORWARDED_PREFIX = "x-forwarded-prefix"; + + + private final ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); + + private MockHttpServletRequest request; + + private MockFilterChain filterChain; + + + @Before + @SuppressWarnings("serial") + public void setUp() throws Exception { + this.request = new MockHttpServletRequest(); + this.request.setScheme("http"); + this.request.setServerName("localhost"); + this.request.setServerPort(80); + this.filterChain = new MockFilterChain(new HttpServlet() {}); + } + + + @Test + public void contextPathEmpty() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, ""); + assertEquals("", filterAndGetContextPath()); + } + + @Test + public void contextPathWithTrailingSlash() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/"); + assertEquals("/foo/bar", filterAndGetContextPath()); + } + + @Test + public void contextPathWithTrailingSlashes() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/foo/bar/baz///"); + assertEquals("/foo/bar/baz", filterAndGetContextPath()); + } + + @Test + public void requestUri() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/"); + this.request.setContextPath("/app"); + this.request.setRequestURI("/app/path"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertEquals("", actual.getContextPath()); + assertEquals("/path", actual.getRequestURI()); + } + + @Test + public void requestUriWithTrailingSlash() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/"); + this.request.setContextPath("/app"); + this.request.setRequestURI("/app/path/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertEquals("", actual.getContextPath()); + assertEquals("/path/", actual.getRequestURI()); + } + @Test + public void requestUriEqualsContextPath() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/"); + this.request.setContextPath("/app"); + this.request.setRequestURI("/app"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertEquals("", actual.getContextPath()); + assertEquals("/", actual.getRequestURI()); + } + + @Test + public void requestUriRootUrl() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/"); + this.request.setContextPath("/app"); + this.request.setRequestURI("/app/"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertEquals("", actual.getContextPath()); + assertEquals("/", actual.getRequestURI()); + } + + @Test + public void caseInsensitiveForwardedPrefix() throws Exception { + this.request = new MockHttpServletRequest() { + + // Make it case-sensitive (SPR-14372) + + @Override + public String getHeader(String header) { + Enumeration<String> names = getHeaderNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + if (name.equals(header)) { + return super.getHeader(header); + } + } + return null; + } + }; + this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + this.request.setRequestURI("/path"); + HttpServletRequest actual = filterAndGetWrappedRequest(); + + assertEquals("/prefix/path", actual.getRequestURI()); + } + + @Test + public void shouldFilter() throws Exception { + testShouldFilter("Forwarded"); + testShouldFilter(X_FORWARDED_HOST); + testShouldFilter(X_FORWARDED_PORT); + testShouldFilter(X_FORWARDED_PROTO); + } + + @Test + public void shouldNotFilter() throws Exception { + assertTrue(this.filter.shouldNotFilter(new MockHttpServletRequest())); + } + + @Test + public void forwardedRequest() throws Exception { + this.request.setRequestURI("/mvc-showcase"); + this.request.addHeader(X_FORWARDED_PROTO, "https"); + this.request.addHeader(X_FORWARDED_HOST, "84.198.58.199"); + this.request.addHeader(X_FORWARDED_PORT, "443"); + this.request.addHeader("foo", "bar"); + + this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); + HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); + + assertEquals("https://84.198.58.199/mvc-showcase", actual.getRequestURL().toString()); + assertEquals("https", actual.getScheme()); + assertEquals("84.198.58.199", actual.getServerName()); + assertEquals(443, actual.getServerPort()); + assertTrue(actual.isSecure()); + + assertNull(actual.getHeader(X_FORWARDED_PROTO)); + assertNull(actual.getHeader(X_FORWARDED_HOST)); + assertNull(actual.getHeader(X_FORWARDED_PORT)); + assertEquals("bar", actual.getHeader("foo")); + } + + @Test + public void requestUriWithForwardedPrefix() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + this.request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL().toString()); + } + + @Test + public void requestUriWithForwardedPrefixTrailingSlash() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); + this.request.setRequestURI("/mvc-showcase"); + + HttpServletRequest actual = filterAndGetWrappedRequest(); + assertEquals("http://localhost/prefix/mvc-showcase", actual.getRequestURL().toString()); + } + + @Test + public void contextPathWithForwardedPrefix() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/prefix"); + this.request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertEquals("/prefix", actual); + } + + @Test + public void contextPathWithForwardedPrefixTrailingSlash() throws Exception { + this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/"); + this.request.setContextPath("/mvc-showcase"); + + String actual = filterAndGetContextPath(); + assertEquals("/prefix", actual); + } + + private String filterAndGetContextPath() throws ServletException, IOException { + return filterAndGetWrappedRequest().getContextPath(); + } + + private HttpServletRequest filterAndGetWrappedRequest() throws ServletException, IOException { + MockHttpServletResponse response = new MockHttpServletResponse(); + this.filter.doFilterInternal(this.request, response, this.filterChain); + return (HttpServletRequest) this.filterChain.getRequest(); + } + + private void testShouldFilter(String headerName) throws ServletException { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(headerName, "1"); + assertFalse(this.filter.shouldNotFilter(request)); + } + +} diff --git a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java index 0c5af485..893501b2 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -74,6 +74,26 @@ public class ShallowEtagHeaderFilterTests { } @Test + public void filterNoMatchWeakETag() throws Exception { + this.filter.setWriteWeakETag(true); + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + final byte[] responseBody = "Hello World".getBytes("UTF-8"); + FilterChain filterChain = (filterRequest, filterResponse) -> { + assertEquals("Invalid request passed", request, filterRequest); + ((HttpServletResponse) filterResponse).setStatus(HttpServletResponse.SC_OK); + FileCopyUtils.copy(responseBody, filterResponse.getOutputStream()); + }; + filter.doFilter(request, response, filterChain); + + assertEquals("Invalid status", 200, response.getStatus()); + assertEquals("Invalid ETag header", "W/\"0b10a8db164e0754105b7a99be72e3fe5\"", response.getHeader("ETag")); + assertTrue("Invalid Content-Length header", response.getContentLength() > 0); + assertArrayEquals("Invalid content", responseBody, response.getContentAsByteArray()); + } + + @Test public void filterMatch() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels"); String etag = "\"0b10a8db164e0754105b7a99be72e3fe5\""; @@ -95,6 +115,27 @@ public class ShallowEtagHeaderFilterTests { } @Test + public void filterMatchWeakEtag() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels"); + String etag = "\"0b10a8db164e0754105b7a99be72e3fe5\""; + request.addHeader("If-None-Match", "W/" + etag); + MockHttpServletResponse response = new MockHttpServletResponse(); + + FilterChain filterChain = (filterRequest, filterResponse) -> { + assertEquals("Invalid request passed", request, filterRequest); + byte[] responseBody = "Hello World".getBytes("UTF-8"); + FileCopyUtils.copy(responseBody, filterResponse.getOutputStream()); + filterResponse.setContentLength(responseBody.length); + }; + filter.doFilter(request, response, filterChain); + + assertEquals("Invalid status", 304, response.getStatus()); + assertEquals("Invalid ETag header", "\"0b10a8db164e0754105b7a99be72e3fe5\"", response.getHeader("ETag")); + assertFalse("Response has Content-Length header", response.containsHeader("Content-Length")); + assertArrayEquals("Invalid content", new byte[0], response.getContentAsByteArray()); + } + + @Test public void filterWriter() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels"); String etag = "\"0b10a8db164e0754105b7a99be72e3fe5\""; diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java index 21fc00e8..2010ba9d 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.tests.sample.beans.TestBean; import org.springframework.validation.BindException; @@ -51,56 +52,56 @@ import static org.mockito.BDDMockito.*; */ public class ModelAttributeMethodProcessorTests { + private NativeWebRequest request; + + private ModelAndViewContainer container; + private ModelAttributeMethodProcessor processor; private MethodParameter paramNamedValidModelAttr; - private MethodParameter paramErrors; - private MethodParameter paramInt; - private MethodParameter paramModelAttr; - + private MethodParameter paramBindingDisabledAttr; private MethodParameter paramNonSimpleType; private MethodParameter returnParamNamedModelAttr; - private MethodParameter returnParamNonSimpleType; - private ModelAndViewContainer mavContainer; - - private NativeWebRequest webRequest; @Before public void setUp() throws Exception { - processor = new ModelAttributeMethodProcessor(false); + this.request = new ServletWebRequest(new MockHttpServletRequest()); + this.container = new ModelAndViewContainer(); + this.processor = new ModelAttributeMethodProcessor(false); Method method = ModelAttributeHandler.class.getDeclaredMethod("modelAttribute", - TestBean.class, Errors.class, int.class, TestBean.class, TestBean.class); + TestBean.class, Errors.class, int.class, TestBean.class, + TestBean.class, TestBean.class); - paramNamedValidModelAttr = new MethodParameter(method, 0); - paramErrors = new MethodParameter(method, 1); - paramInt = new MethodParameter(method, 2); - paramModelAttr = new MethodParameter(method, 3); - paramNonSimpleType = new MethodParameter(method, 4); + this.paramNamedValidModelAttr = new SynthesizingMethodParameter(method, 0); + this.paramErrors = new SynthesizingMethodParameter(method, 1); + this.paramInt = new SynthesizingMethodParameter(method, 2); + this.paramModelAttr = new SynthesizingMethodParameter(method, 3); + this.paramBindingDisabledAttr = new SynthesizingMethodParameter(method, 4); + this.paramNonSimpleType = new SynthesizingMethodParameter(method, 5); - returnParamNamedModelAttr = new MethodParameter(getClass().getDeclaredMethod("annotatedReturnValue"), -1); - returnParamNonSimpleType = new MethodParameter(getClass().getDeclaredMethod("notAnnotatedReturnValue"), -1); + method = getClass().getDeclaredMethod("annotatedReturnValue"); + this.returnParamNamedModelAttr = new MethodParameter(method, -1); - mavContainer = new ModelAndViewContainer(); - - webRequest = new ServletWebRequest(new MockHttpServletRequest()); + method = getClass().getDeclaredMethod("notAnnotatedReturnValue"); + this.returnParamNonSimpleType = new MethodParameter(method, -1); } + @Test public void supportedParameters() throws Exception { - // Only @ModelAttribute arguments - assertTrue(processor.supportsParameter(paramNamedValidModelAttr)); - assertTrue(processor.supportsParameter(paramModelAttr)); + assertTrue(this.processor.supportsParameter(this.paramNamedValidModelAttr)); + assertTrue(this.processor.supportsParameter(this.paramModelAttr)); - assertFalse(processor.supportsParameter(paramErrors)); - assertFalse(processor.supportsParameter(paramInt)); - assertFalse(processor.supportsParameter(paramNonSimpleType)); + assertFalse(this.processor.supportsParameter(this.paramErrors)); + assertFalse(this.processor.supportsParameter(this.paramInt)); + assertFalse(this.processor.supportsParameter(this.paramNonSimpleType)); } @Test @@ -108,135 +109,162 @@ public class ModelAttributeMethodProcessorTests { processor = new ModelAttributeMethodProcessor(true); // Only non-simple types, even if not annotated - assertTrue(processor.supportsParameter(paramNamedValidModelAttr)); - assertTrue(processor.supportsParameter(paramErrors)); - assertTrue(processor.supportsParameter(paramModelAttr)); - assertTrue(processor.supportsParameter(paramNonSimpleType)); + assertTrue(this.processor.supportsParameter(this.paramNamedValidModelAttr)); + assertTrue(this.processor.supportsParameter(this.paramErrors)); + assertTrue(this.processor.supportsParameter(this.paramModelAttr)); + assertTrue(this.processor.supportsParameter(this.paramNonSimpleType)); - assertFalse(processor.supportsParameter(paramInt)); + assertFalse(this.processor.supportsParameter(this.paramInt)); } @Test public void supportedReturnTypes() throws Exception { processor = new ModelAttributeMethodProcessor(false); - assertTrue(processor.supportsReturnType(returnParamNamedModelAttr)); - assertFalse(processor.supportsReturnType(returnParamNonSimpleType)); + assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr)); + assertFalse(this.processor.supportsReturnType(returnParamNonSimpleType)); } @Test public void supportedReturnTypesInDefaultResolutionMode() throws Exception { processor = new ModelAttributeMethodProcessor(true); - assertTrue(processor.supportsReturnType(returnParamNamedModelAttr)); - assertTrue(processor.supportsReturnType(returnParamNonSimpleType)); + assertTrue(this.processor.supportsReturnType(returnParamNamedModelAttr)); + assertTrue(this.processor.supportsReturnType(returnParamNonSimpleType)); } @Test public void bindExceptionRequired() throws Exception { - assertTrue(processor.isBindExceptionRequired(null, paramNonSimpleType)); + assertTrue(this.processor.isBindExceptionRequired(null, this.paramNonSimpleType)); + assertFalse(this.processor.isBindExceptionRequired(null, this.paramNamedValidModelAttr)); } @Test - public void bindExceptionNotRequired() throws Exception { - assertFalse(processor.isBindExceptionRequired(null, paramNamedValidModelAttr)); + public void resolveArgumentFromModel() throws Exception { + testGetAttributeFromModel("attrName", this.paramNamedValidModelAttr); + testGetAttributeFromModel("testBean", this.paramModelAttr); + testGetAttributeFromModel("testBean", this.paramNonSimpleType); } @Test - public void resovleArgumentFromModel() throws Exception { - getAttributeFromModel("attrName", paramNamedValidModelAttr); - getAttributeFromModel("testBean", paramModelAttr); - getAttributeFromModel("testBean", paramNonSimpleType); + public void resovleArgumentViaDefaultConstructor() throws Exception { + WebDataBinder dataBinder = new WebRequestDataBinder(null); + WebDataBinderFactory factory = mock(WebDataBinderFactory.class); + given(factory.createBinder(anyObject(), notNull(), eq("attrName"))).willReturn(dataBinder); + + this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory); + verify(factory).createBinder(anyObject(), notNull(), eq("attrName")); } - private void getAttributeFromModel(String expectedAttributeName, MethodParameter param) throws Exception { + @Test + public void resolveArgumentValidation() throws Exception { + String name = "attrName"; Object target = new TestBean(); - mavContainer.addAttribute(expectedAttributeName, target); + this.container.addAttribute(name, target); - WebDataBinder dataBinder = new WebRequestDataBinder(target); + StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); WebDataBinderFactory factory = mock(WebDataBinderFactory.class); - given(factory.createBinder(webRequest, target, expectedAttributeName)).willReturn(dataBinder); + given(factory.createBinder(this.request, target, name)).willReturn(dataBinder); - processor.resolveArgument(param, mavContainer, webRequest, factory); - verify(factory).createBinder(webRequest, target, expectedAttributeName); + this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory); + + assertTrue(dataBinder.isBindInvoked()); + assertTrue(dataBinder.isValidateInvoked()); } @Test - public void resovleArgumentViaDefaultConstructor() throws Exception { - WebDataBinder dataBinder = new WebRequestDataBinder(null); + public void resolveArgumentBindingDisabledPreviously() throws Exception { + String name = "attrName"; + Object target = new TestBean(); + this.container.addAttribute(name, target); + + // Declare binding disabled (e.g. via @ModelAttribute method) + this.container.setBindingDisabled(name); + StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); WebDataBinderFactory factory = mock(WebDataBinderFactory.class); - given(factory.createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName"))).willReturn(dataBinder); + given(factory.createBinder(this.request, target, name)).willReturn(dataBinder); - processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, factory); + this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory); - verify(factory).createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName")); + assertFalse(dataBinder.isBindInvoked()); + assertTrue(dataBinder.isValidateInvoked()); } @Test - public void resolveArgumentValidation() throws Exception { - String name = "attrName"; + public void resolveArgumentBindingDisabled() throws Exception { + String name = "noBindAttr"; Object target = new TestBean(); - mavContainer.addAttribute(name, target); + this.container.addAttribute(name, target); StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); - WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); - given(binderFactory.createBinder(webRequest, target, name)).willReturn(dataBinder); + WebDataBinderFactory factory = mock(WebDataBinderFactory.class); + given(factory.createBinder(this.request, target, name)).willReturn(dataBinder); - processor.resolveArgument(paramNamedValidModelAttr, mavContainer, webRequest, binderFactory); + this.processor.resolveArgument(this.paramBindingDisabledAttr, this.container, this.request, factory); - assertTrue(dataBinder.isBindInvoked()); + assertFalse(dataBinder.isBindInvoked()); assertTrue(dataBinder.isValidateInvoked()); } @Test(expected = BindException.class) - public void resovleArgumentBindException() throws Exception { + public void resolveArgumentBindException() throws Exception { String name = "testBean"; Object target = new TestBean(); - mavContainer.getModel().addAttribute(target); + this.container.getModel().addAttribute(target); StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); dataBinder.getBindingResult().reject("error"); - WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); - given(binderFactory.createBinder(webRequest, target, name)).willReturn(dataBinder); + given(binderFactory.createBinder(this.request, target, name)).willReturn(dataBinder); - processor.resolveArgument(paramNonSimpleType, mavContainer, webRequest, binderFactory); - verify(binderFactory).createBinder(webRequest, target, name); + this.processor.resolveArgument(this.paramNonSimpleType, this.container, this.request, binderFactory); + verify(binderFactory).createBinder(this.request, target, name); } @Test // SPR-9378 public void resolveArgumentOrdering() throws Exception { String name = "testBean"; Object testBean = new TestBean(name); - mavContainer.addAttribute(name, testBean); - mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, testBean); + this.container.addAttribute(name, testBean); + this.container.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, testBean); Object anotherTestBean = new TestBean(); - mavContainer.addAttribute("anotherTestBean", anotherTestBean); + this.container.addAttribute("anotherTestBean", anotherTestBean); StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name); WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); - given(binderFactory.createBinder(webRequest, testBean, name)).willReturn(dataBinder); + given(binderFactory.createBinder(this.request, testBean, name)).willReturn(dataBinder); - processor.resolveArgument(paramModelAttr, mavContainer, webRequest, binderFactory); + this.processor.resolveArgument(this.paramModelAttr, this.container, this.request, binderFactory); - assertSame("Resolved attribute should be updated to be last in the order", - testBean, mavContainer.getModel().values().toArray()[1]); - assertSame("BindingResult of resolved attribute should be last in the order", - dataBinder.getBindingResult(), mavContainer.getModel().values().toArray()[2]); + Object[] values = this.container.getModel().values().toArray(); + assertSame("Resolved attribute should be updated to be last", testBean, values[1]); + assertSame("BindingResult of resolved attr should be last", dataBinder.getBindingResult(), values[2]); } @Test public void handleAnnotatedReturnValue() throws Exception { - processor.handleReturnValue("expected", returnParamNamedModelAttr, mavContainer, webRequest); - assertEquals("expected", mavContainer.getModel().get("modelAttrName")); + this.processor.handleReturnValue("expected", this.returnParamNamedModelAttr, this.container, this.request); + assertEquals("expected", this.container.getModel().get("modelAttrName")); } @Test public void handleNotAnnotatedReturnValue() throws Exception { TestBean testBean = new TestBean("expected"); - processor.handleReturnValue(testBean, returnParamNonSimpleType, mavContainer, webRequest); + this.processor.handleReturnValue(testBean, this.returnParamNonSimpleType, this.container, this.request); + assertSame(testBean, this.container.getModel().get("testBean")); + } + - assertSame(testBean, mavContainer.getModel().get("testBean")); + private void testGetAttributeFromModel(String expectedAttrName, MethodParameter param) throws Exception { + Object target = new TestBean(); + this.container.addAttribute(expectedAttrName, target); + + WebDataBinder dataBinder = new WebRequestDataBinder(target); + WebDataBinderFactory factory = mock(WebDataBinderFactory.class); + given(factory.createBinder(this.request, target, expectedAttrName)).willReturn(dataBinder); + + this.processor.resolveArgument(param, this.container, this.request, factory); + verify(factory).createBinder(this.request, target, expectedAttrName); } @@ -246,6 +274,7 @@ public class ModelAttributeMethodProcessorTests { private boolean validateInvoked; + public StubRequestDataBinder(Object target, String objectName) { super(target, objectName); } @@ -285,13 +314,18 @@ public class ModelAttributeMethodProcessorTests { private static class ModelAttributeHandler { @SuppressWarnings("unused") - public void modelAttribute(@ModelAttribute("attrName") @Valid TestBean annotatedAttr, Errors errors, - int intArg, @ModelAttribute TestBean defaultNameAttr, TestBean notAnnotatedAttr) { + public void modelAttribute( + @ModelAttribute("attrName") @Valid TestBean annotatedAttr, + Errors errors, + int intArg, + @ModelAttribute TestBean defaultNameAttr, + @ModelAttribute(name="noBindAttr", binding=false) @Valid TestBean noBindAttr, + TestBean notAnnotatedAttr) { } } - @ModelAttribute("modelAttrName") + @ModelAttribute("modelAttrName") @SuppressWarnings("unused") private String annotatedReturnValue() { return null; } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java index e0a3c69f..24c61825 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,19 +16,12 @@ package org.springframework.web.method.annotation; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.mock; - import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.Collections; import org.junit.Before; import org.junit.Test; + import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.ui.Model; @@ -43,10 +36,19 @@ import org.springframework.web.bind.support.SessionAttributeStore; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.InvocableHandlerMethod; import org.springframework.web.method.support.ModelAndViewContainer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; + /** * Text fixture for {@link ModelFactory} tests. @@ -55,103 +57,116 @@ import org.springframework.web.method.support.ModelAndViewContainer; */ public class ModelFactoryTests { - private TestController controller = new TestController(); + private NativeWebRequest webRequest; - private InvocableHandlerMethod handleMethod; + private SessionAttributesHandler attributeHandler; - private InvocableHandlerMethod handleSessionAttrMethod; + private SessionAttributeStore attributeStore; - private SessionAttributesHandler sessionAttrsHandler; + private TestController controller = new TestController(); - private SessionAttributeStore sessionAttributeStore; - - private NativeWebRequest webRequest; + private ModelAndViewContainer mavContainer; @Before public void setUp() throws Exception { - this.controller = new TestController(); - - Method method = TestController.class.getDeclaredMethod("handle"); - this.handleMethod = new InvocableHandlerMethod(this.controller, method); - - method = TestController.class.getDeclaredMethod("handleSessionAttr", String.class); - this.handleSessionAttrMethod = new InvocableHandlerMethod(this.controller, method); - - this.sessionAttributeStore = new DefaultSessionAttributeStore(); - this.sessionAttrsHandler = new SessionAttributesHandler(TestController.class, this.sessionAttributeStore); this.webRequest = new ServletWebRequest(new MockHttpServletRequest()); + this.attributeStore = new DefaultSessionAttributeStore(); + this.attributeHandler = new SessionAttributesHandler(TestController.class, this.attributeStore); + this.controller = new TestController(); + this.mavContainer = new ModelAndViewContainer(); } @Test public void modelAttributeMethod() throws Exception { ModelFactory modelFactory = createModelFactory("modelAttr", Model.class); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); - assertEquals(Boolean.TRUE, mavContainer.getModel().get("modelAttr")); + assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("modelAttr")); } @Test public void modelAttributeMethodWithExplicitName() throws Exception { ModelFactory modelFactory = createModelFactory("modelAttrWithName"); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); - assertEquals(Boolean.TRUE, mavContainer.getModel().get("name")); + assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("name")); } @Test public void modelAttributeMethodWithNameByConvention() throws Exception { ModelFactory modelFactory = createModelFactory("modelAttrConvention"); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); - assertEquals(Boolean.TRUE, mavContainer.getModel().get("boolean")); + assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("boolean")); } @Test public void modelAttributeMethodWithNullReturnValue() throws Exception { ModelFactory modelFactory = createModelFactory("nullModelAttr"); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); - assertTrue(mavContainer.containsAttribute("name")); - assertNull(mavContainer.getModel().get("name")); + assertTrue(this.mavContainer.containsAttribute("name")); + assertNull(this.mavContainer.getModel().get("name")); } @Test - public void sessionAttribute() throws Exception { - this.sessionAttributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue"); + public void modelAttributeWithBindingDisabled() throws Exception { + ModelFactory modelFactory = createModelFactory("modelAttrWithBindingDisabled"); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); + + assertTrue(this.mavContainer.containsAttribute("foo")); + assertTrue(this.mavContainer.isBindingDisabled("foo")); + } + + @Test + public void modelAttributeFromSessionWithBindingDisabled() throws Exception { + Foo foo = new Foo(); + this.attributeStore.storeAttribute(this.webRequest, "foo", foo); - // Resolve successfully handler session attribute once - assertTrue(sessionAttrsHandler.isHandlerSessionAttribute("sessionAttr", null)); + ModelFactory modelFactory = createModelFactory("modelAttrWithBindingDisabled"); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); + + assertTrue(this.mavContainer.containsAttribute("foo")); + assertSame(foo, this.mavContainer.getModel().get("foo")); + assertTrue(this.mavContainer.isBindingDisabled("foo")); + } + + @Test + public void sessionAttribute() throws Exception { + this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue"); ModelFactory modelFactory = createModelFactory("modelAttr", Model.class); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleMethod); + HandlerMethod handlerMethod = createHandlerMethod("handle"); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); - assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr")); + assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr")); } @Test public void sessionAttributeNotPresent() throws Exception { - ModelFactory modelFactory = new ModelFactory(null, null, this.sessionAttrsHandler); - + ModelFactory modelFactory = new ModelFactory(null, null, this.attributeHandler); + HandlerMethod handlerMethod = createHandlerMethod("handleSessionAttr", String.class); try { - modelFactory.initModel(this.webRequest, new ModelAndViewContainer(), this.handleSessionAttrMethod); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); fail("Expected HttpSessionRequiredException"); } catch (HttpSessionRequiredException e) { // expected } - this.sessionAttributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue"); - ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - modelFactory.initModel(this.webRequest, mavContainer, this.handleSessionAttrMethod); + // Now add attribute and try again + this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue"); - assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr")); + modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod); + assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr")); } @Test @@ -165,11 +180,12 @@ public class ModelFactoryTests { WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); given(binderFactory.createBinder(this.webRequest, command, commandName)).willReturn(dataBinder); - ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler); + ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler); modelFactory.updateModel(this.webRequest, container); assertEquals(command, container.getModel().get(commandName)); - assertSame(dataBinder.getBindingResult(), container.getModel().get(bindingResultKey(commandName))); + String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + commandName; + assertSame(dataBinder.getBindingResult(), container.getModel().get(bindingResultKey)); assertEquals(2, container.getModel().size()); } @@ -184,11 +200,11 @@ public class ModelFactoryTests { WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder); - ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler); + ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler); modelFactory.updateModel(this.webRequest, container); assertEquals(attribute, container.getModel().get(attributeName)); - assertEquals(attribute, this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName)); + assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName)); } @Test @@ -198,9 +214,7 @@ public class ModelFactoryTests { ModelAndViewContainer container = new ModelAndViewContainer(); container.addAttribute(attributeName, attribute); - // Store and resolve once (to be "remembered") - this.sessionAttributeStore.storeAttribute(this.webRequest, attributeName, attribute); - this.sessionAttrsHandler.isHandlerSessionAttribute(attributeName, null); + this.attributeStore.storeAttribute(this.webRequest, attributeName, attribute); WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName); WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); @@ -208,11 +222,11 @@ public class ModelFactoryTests { container.getSessionStatus().setComplete(); - ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler); + ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler); modelFactory.updateModel(this.webRequest, container); assertEquals(attribute, container.getModel().get(attributeName)); - assertNull(this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName)); + assertNull(this.attributeStore.retrieveAttribute(this.webRequest, attributeName)); } // SPR-12542 @@ -233,34 +247,34 @@ public class ModelFactoryTests { WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder); - ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler); + ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler); modelFactory.updateModel(this.webRequest, container); assertEquals(queryParam, container.getModel().get(queryParamName)); assertEquals(1, container.getModel().size()); - assertEquals(attribute, this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName)); + assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName)); } - private String bindingResultKey(String key) { - return BindingResult.MODEL_KEY_PREFIX + key; - } - - private ModelFactory createModelFactory(String methodName, Class<?>... parameterTypes) throws Exception{ - Method method = TestController.class.getMethod(methodName, parameterTypes); + private ModelFactory createModelFactory(String methodName, Class<?>... parameterTypes) throws Exception { + HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); + resolvers.addResolver(new ModelMethodProcessor()); - HandlerMethodArgumentResolverComposite argResolvers = new HandlerMethodArgumentResolverComposite(); - argResolvers.addResolver(new ModelMethodProcessor()); + InvocableHandlerMethod modelMethod = createHandlerMethod(methodName, parameterTypes); + modelMethod.setHandlerMethodArgumentResolvers(resolvers); + modelMethod.setDataBinderFactory(null); + modelMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer()); - InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(this.controller, method); - handlerMethod.setHandlerMethodArgumentResolvers(argResolvers); - handlerMethod.setDataBinderFactory(null); - handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer()); + return new ModelFactory(Collections.singletonList(modelMethod), null, this.attributeHandler); + } - return new ModelFactory(Arrays.asList(handlerMethod), null, this.sessionAttrsHandler); + private InvocableHandlerMethod createHandlerMethod(String methodName, Class<?>... paramTypes) throws Exception { + Method method = this.controller.getClass().getMethod(methodName, paramTypes); + return new InvocableHandlerMethod(this.controller, method); } - @SessionAttributes("sessionAttr") @SuppressWarnings("unused") + + @SessionAttributes({"sessionAttr", "foo"}) @SuppressWarnings("unused") private static class TestController { @ModelAttribute @@ -283,6 +297,11 @@ public class ModelFactoryTests { return null; } + @ModelAttribute(name="foo", binding=false) + public Foo modelAttrWithBindingDisabled() { + return new Foo(); + } + public void handle() { } @@ -290,4 +309,7 @@ public class ModelFactoryTests { } } + private static class Foo { + } + } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java index b484e2ff..a453abb0 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,6 +17,9 @@ package org.springframework.web.method.annotation; import java.lang.reflect.Method; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.Map; import org.junit.After; @@ -25,10 +28,14 @@ import org.junit.Test; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.bind.support.DefaultDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletWebRequest; @@ -50,7 +57,11 @@ public class RequestHeaderMethodArgumentResolverTests { private MethodParameter paramNamedValueStringArray; private MethodParameter paramSystemProperty; private MethodParameter paramContextPath; + private MethodParameter paramResolvedNameWithExpression; + private MethodParameter paramResolvedNameWithPlaceholder; private MethodParameter paramNamedValueMap; + private MethodParameter paramDate; + private MethodParameter paramInstant; private MockHttpServletRequest servletRequest; @@ -64,12 +75,16 @@ public class RequestHeaderMethodArgumentResolverTests { context.refresh(); resolver = new RequestHeaderMethodArgumentResolver(context.getBeanFactory()); - Method method = getClass().getMethod("params", String.class, String[].class, String.class, String.class, Map.class); + Method method = ReflectionUtils.findMethod(getClass(), "params", (Class<?>[]) null); paramNamedDefaultValueStringHeader = new SynthesizingMethodParameter(method, 0); paramNamedValueStringArray = new SynthesizingMethodParameter(method, 1); paramSystemProperty = new SynthesizingMethodParameter(method, 2); paramContextPath = new SynthesizingMethodParameter(method, 3); - paramNamedValueMap = new SynthesizingMethodParameter(method, 4); + paramResolvedNameWithExpression = new SynthesizingMethodParameter(method, 4); + paramResolvedNameWithPlaceholder = new SynthesizingMethodParameter(method, 5); + paramNamedValueMap = new SynthesizingMethodParameter(method, 6); + paramDate = new SynthesizingMethodParameter(method, 7); + paramInstant = new SynthesizingMethodParameter(method, 8); servletRequest = new MockHttpServletRequest(); webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse()); @@ -97,45 +112,77 @@ public class RequestHeaderMethodArgumentResolverTests { servletRequest.addHeader("name", expected); Object result = resolver.resolveArgument(paramNamedDefaultValueStringHeader, null, webRequest, null); - assertTrue(result instanceof String); - assertEquals("Invalid result", expected, result); + assertEquals(expected, result); } @Test public void resolveStringArrayArgument() throws Exception { - String[] expected = new String[]{"foo", "bar"}; + String[] expected = new String[] {"foo", "bar"}; servletRequest.addHeader("name", expected); Object result = resolver.resolveArgument(paramNamedValueStringArray, null, webRequest, null); - assertTrue(result instanceof String[]); - assertArrayEquals("Invalid result", expected, (String[]) result); + assertArrayEquals(expected, (String[]) result); } @Test public void resolveDefaultValue() throws Exception { Object result = resolver.resolveArgument(paramNamedDefaultValueStringHeader, null, webRequest, null); - assertTrue(result instanceof String); - assertEquals("Invalid result", "bar", result); + assertEquals("bar", result); } @Test public void resolveDefaultValueFromSystemProperty() throws Exception { System.setProperty("systemProperty", "bar"); - Object result = resolver.resolveArgument(paramSystemProperty, null, webRequest, null); - System.clearProperty("systemProperty"); + try { + Object result = resolver.resolveArgument(paramSystemProperty, null, webRequest, null); + assertTrue(result instanceof String); + assertEquals("bar", result); + } + finally { + System.clearProperty("systemProperty"); + } + } - assertTrue(result instanceof String); - assertEquals("bar", result); + @Test + public void resolveNameFromSystemPropertyThroughExpression() throws Exception { + String expected = "foo"; + servletRequest.addHeader("bar", expected); + + System.setProperty("systemProperty", "bar"); + try { + Object result = resolver.resolveArgument(paramResolvedNameWithExpression, null, webRequest, null); + assertTrue(result instanceof String); + assertEquals(expected, result); + } + finally { + System.clearProperty("systemProperty"); + } + } + + @Test + public void resolveNameFromSystemPropertyThroughPlaceholder() throws Exception { + String expected = "foo"; + servletRequest.addHeader("bar", expected); + + System.setProperty("systemProperty", "bar"); + try { + Object result = resolver.resolveArgument(paramResolvedNameWithPlaceholder, null, webRequest, null); + assertTrue(result instanceof String); + assertEquals(expected, result); + } + finally { + System.clearProperty("systemProperty"); + } } @Test public void resolveDefaultValueFromRequest() throws Exception { servletRequest.setContextPath("/bar"); - Object result = resolver.resolveArgument(paramContextPath, null, webRequest, null); + Object result = resolver.resolveArgument(paramContextPath, null, webRequest, null); assertTrue(result instanceof String); assertEquals("/bar", result); } @@ -145,12 +192,46 @@ public class RequestHeaderMethodArgumentResolverTests { resolver.resolveArgument(paramNamedValueStringArray, null, webRequest, null); } + @Test + @SuppressWarnings("deprecation") + public void dateConversion() throws Exception { + String rfc1123val = "Thu, 21 Apr 2016 17:11:08 +0100"; + servletRequest.addHeader("name", rfc1123val); + + ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer(); + bindingInitializer.setConversionService(new DefaultFormattingConversionService()); + Object result = resolver.resolveArgument(paramDate, null, webRequest, + new DefaultDataBinderFactory(bindingInitializer)); + + assertTrue(result instanceof Date); + assertEquals(new Date(rfc1123val), result); + } + + @Test + public void instantConversion() throws Exception { + String rfc1123val = "Thu, 21 Apr 2016 17:11:08 +0100"; + servletRequest.addHeader("name", rfc1123val); + + ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer(); + bindingInitializer.setConversionService(new DefaultFormattingConversionService()); + Object result = resolver.resolveArgument(paramInstant, null, webRequest, + new DefaultDataBinderFactory(bindingInitializer)); + + assertTrue(result instanceof Instant); + assertEquals(Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(rfc1123val)), result); + } + - public void params(@RequestHeader(name = "name", defaultValue = "bar") String param1, - @RequestHeader("name") String[] param2, - @RequestHeader(name = "name", defaultValue="#{systemProperties.systemProperty}") String param3, - @RequestHeader(name = "name", defaultValue="#{request.contextPath}") String param4, - @RequestHeader("name") Map<?, ?> unsupported) { + public void params( + @RequestHeader(name = "name", defaultValue = "bar") String param1, + @RequestHeader("name") String[] param2, + @RequestHeader(name = "name", defaultValue="#{systemProperties.systemProperty}") String param3, + @RequestHeader(name = "name", defaultValue="#{request.contextPath}") String param4, + @RequestHeader("#{systemProperties.systemProperty}") String param5, + @RequestHeader("${systemProperty}") String param6, + @RequestHeader("name") Map<?, ?> unsupported, + @RequestHeader("name") Date dateParam, + @RequestHeader("name") Instant instantParam) { } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java index 00b61b5f..f77ce2bc 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java @@ -37,6 +37,7 @@ import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockMultipartFile; import org.springframework.mock.web.test.MockMultipartHttpServletRequest; import org.springframework.mock.web.test.MockPart; +import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestParam; @@ -49,6 +50,7 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.support.MissingServletRequestPartException; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; @@ -82,6 +84,7 @@ public class RequestParamMethodArgumentResolverTests { private MethodParameter paramRequired; private MethodParameter paramNotRequired; private MethodParameter paramOptional; + private MethodParameter multipartFileOptional; private NativeWebRequest webRequest; @@ -92,12 +95,7 @@ public class RequestParamMethodArgumentResolverTests { public void setUp() throws Exception { resolver = new RequestParamMethodArgumentResolver(null, true); ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); - - Method method = getClass().getMethod("params", String.class, String[].class, - Map.class, MultipartFile.class, List.class, MultipartFile[].class, - Part.class, List.class, Part[].class, Map.class, - String.class, MultipartFile.class, List.class, Part.class, - MultipartFile.class, String.class, String.class, Optional.class); + Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class<?>[]) null); paramNamedDefaultValueString = new SynthesizingMethodParameter(method, 0); paramNamedStringArray = new SynthesizingMethodParameter(method, 1); @@ -121,6 +119,7 @@ public class RequestParamMethodArgumentResolverTests { paramRequired = new SynthesizingMethodParameter(method, 15); paramNotRequired = new SynthesizingMethodParameter(method, 16); paramOptional = new SynthesizingMethodParameter(method, 17); + multipartFileOptional = new SynthesizingMethodParameter(method, 18); request = new MockHttpServletRequest(); webRequest = new ServletWebRequest(request, new MockHttpServletResponse()); @@ -130,19 +129,25 @@ public class RequestParamMethodArgumentResolverTests { @Test public void supportsParameter() { resolver = new RequestParamMethodArgumentResolver(null, true); - assertTrue("String parameter not supported", resolver.supportsParameter(paramNamedDefaultValueString)); - assertTrue("String array parameter not supported", resolver.supportsParameter(paramNamedStringArray)); - assertTrue("Named map not parameter supported", resolver.supportsParameter(paramNamedMap)); - assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFile)); - assertTrue("List<MultipartFile> parameter not supported", resolver.supportsParameter(paramMultipartFileList)); - assertTrue("MultipartFile[] parameter not supported", resolver.supportsParameter(paramMultipartFileArray)); - assertTrue("Part parameter not supported", resolver.supportsParameter(paramPart)); - assertTrue("List<Part> parameter not supported", resolver.supportsParameter(paramPartList)); - assertTrue("Part[] parameter not supported", resolver.supportsParameter(paramPartArray)); - assertFalse("non-@RequestParam parameter supported", resolver.supportsParameter(paramMap)); - assertTrue("Simple type params supported w/o annotations", resolver.supportsParameter(paramStringNotAnnot)); - assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFileNotAnnot)); - assertTrue("Part parameter not supported", resolver.supportsParameter(paramPartNotAnnot)); + assertTrue(resolver.supportsParameter(paramNamedDefaultValueString)); + assertTrue(resolver.supportsParameter(paramNamedStringArray)); + assertTrue(resolver.supportsParameter(paramNamedMap)); + assertTrue(resolver.supportsParameter(paramMultipartFile)); + assertTrue(resolver.supportsParameter(paramMultipartFileList)); + assertTrue(resolver.supportsParameter(paramMultipartFileArray)); + assertTrue(resolver.supportsParameter(paramPart)); + assertTrue(resolver.supportsParameter(paramPartList)); + assertTrue(resolver.supportsParameter(paramPartArray)); + assertFalse(resolver.supportsParameter(paramMap)); + assertTrue(resolver.supportsParameter(paramStringNotAnnot)); + assertTrue(resolver.supportsParameter(paramMultipartFileNotAnnot)); + assertTrue(resolver.supportsParameter(paramMultipartFileListNotAnnot)); + assertTrue(resolver.supportsParameter(paramPartNotAnnot)); + assertFalse(resolver.supportsParameter(paramRequestPartAnnot)); + assertTrue(resolver.supportsParameter(paramRequired)); + assertTrue(resolver.supportsParameter(paramNotRequired)); + assertTrue(resolver.supportsParameter(paramOptional)); + assertTrue(resolver.supportsParameter(multipartFileOptional)); resolver = new RequestParamMethodArgumentResolver(null, false); assertFalse(resolver.supportsParameter(paramStringNotAnnot)); @@ -188,6 +193,7 @@ public class RequestParamMethodArgumentResolverTests { MultipartFile expected2 = new MockMultipartFile("mfilelist", "Hello World 2".getBytes()); request.addFile(expected1); request.addFile(expected2); + request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes())); webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null); @@ -202,6 +208,7 @@ public class RequestParamMethodArgumentResolverTests { MultipartFile expected2 = new MockMultipartFile("mfilearray", "Hello World 2".getBytes()); request.addFile(expected1); request.addFile(expected2); + request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes())); webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileArray, null, webRequest, null); @@ -222,7 +229,6 @@ public class RequestParamMethodArgumentResolverTests { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPart, null, webRequest, null); - assertTrue(result instanceof Part); assertEquals("Invalid result", expected, result); } @@ -230,12 +236,13 @@ public class RequestParamMethodArgumentResolverTests { @Test public void resolvePartList() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - MockPart expected1 = new MockPart("pfilelist", "Hello World 1".getBytes()); - MockPart expected2 = new MockPart("pfilelist", "Hello World 2".getBytes()); request.setMethod("POST"); request.setContentType("multipart/form-data"); + MockPart expected1 = new MockPart("pfilelist", "Hello World 1".getBytes()); + MockPart expected2 = new MockPart("pfilelist", "Hello World 2".getBytes()); request.addPart(expected1); request.addPart(expected2); + request.addPart(new MockPart("other", "Hello World 3".getBytes())); webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartList, null, webRequest, null); @@ -252,6 +259,7 @@ public class RequestParamMethodArgumentResolverTests { request.setContentType("multipart/form-data"); request.addPart(expected1); request.addPart(expected2); + request.addPart(new MockPart("other", "Hello World 3".getBytes())); webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null); @@ -307,12 +315,19 @@ public class RequestParamMethodArgumentResolverTests { assertEquals(expected, ((List<?>) actual).get(0)); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = MultipartException.class) + public void noMultipartContent() throws Exception { + request.setMethod("POST"); + resolver.resolveArgument(paramMultipartFile, null, webRequest, null); + fail("Expected exception: no multipart content"); + } + + @Test(expected = MissingServletRequestPartException.class) public void missingMultipartFile() throws Exception { request.setMethod("POST"); request.setContentType("multipart/form-data"); resolver.resolveArgument(paramMultipartFile, null, webRequest, null); - fail("Expected exception: request is not MultiPartHttpServletRequest but param is MultipartFile"); + fail("Expected exception: no such part found"); } @Test @@ -422,8 +437,45 @@ public class RequestParamMethodArgumentResolverTests { assertEquals(123, ((Optional) result).get()); } + @Test + public void resolveOptionalMultipartFile() throws Exception { + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + MultipartFile expected = new MockMultipartFile("mfile", "Hello World".getBytes()); + request.addFile(expected); + webRequest = new ServletWebRequest(request); + + Object result = resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory); + assertTrue(result instanceof Optional); + assertEquals("Invalid result", expected, ((Optional<?>) result).get()); + } + + @Test + public void missingOptionalMultipartFile() throws Exception { + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + request.setMethod("POST"); + request.setContentType("multipart/form-data"); + assertEquals(Optional.empty(), resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory)); + } + + @Test + public void optionalMultipartFileWithoutMultipartRequest() throws Exception { + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + assertEquals(Optional.empty(), resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory)); + } + - public void params(@RequestParam(name = "name", defaultValue = "bar") String param1, + public void handle( + @RequestParam(name = "name", defaultValue = "bar") String param1, @RequestParam("name") String[] param2, @RequestParam("name") Map<?, ?> param3, @RequestParam("mfile") MultipartFile param4, @@ -440,7 +492,8 @@ public class RequestParamMethodArgumentResolverTests { @RequestPart MultipartFile requestPartAnnot, @RequestParam("name") String paramRequired, @RequestParam(name = "name", required = false) String paramNotRequired, - @RequestParam("name") Optional<Integer> paramOptional) { + @RequestParam("name") Optional<Integer> paramOptional, + @RequestParam("mfile") Optional<MultipartFile> multipartFileOptional) { } } diff --git a/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java b/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java index 1642351e..09f3700a 100644 --- a/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/DefaultUriTemplateHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -15,74 +15,135 @@ */ package org.springframework.web.util; -import static org.junit.Assert.assertEquals; - import java.net.URI; import java.util.HashMap; import java.util.Map; -import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; + /** * Unit tests for {@link DefaultUriTemplateHandler}. + * * @author Rossen Stoyanchev */ public class DefaultUriTemplateHandlerTests { - private DefaultUriTemplateHandler handler; - - - @Before - public void setUp() throws Exception { - this.handler = new DefaultUriTemplateHandler(); - } + private final DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler(); @Test - public void baseUrl() throws Exception { + public void baseUrlWithoutPath() throws Exception { this.handler.setBaseUrl("http://localhost:8080"); URI actual = this.handler.expand("/myapiresource"); - URI expected = new URI("http://localhost:8080/myapiresource"); - assertEquals(expected, actual); + assertEquals("http://localhost:8080/myapiresource", actual.toString()); } @Test - public void baseUrlWithPartialPath() throws Exception { + public void baseUrlWithPath() throws Exception { this.handler.setBaseUrl("http://localhost:8080/context"); URI actual = this.handler.expand("/myapiresource"); - URI expected = new URI("http://localhost:8080/context/myapiresource"); - assertEquals(expected, actual); + assertEquals("http://localhost:8080/context/myapiresource", actual.toString()); + } + + @Test // SPR-14147 + public void defaultUriVariables() throws Exception { + Map<String, String> defaultVars = new HashMap<>(2); + defaultVars.put("host", "api.example.com"); + defaultVars.put("port", "443"); + this.handler.setDefaultUriVariables(defaultVars); + + Map<String, Object> vars = new HashMap<>(1); + vars.put("id", 123L); + + String template = "https://{host}:{port}/v42/customers/{id}"; + URI actual = this.handler.expand(template, vars); + + assertEquals("https://api.example.com:443/v42/customers/123", actual.toString()); } @Test - public void expandWithFullPath() throws Exception { - Map<String, String> vars = new HashMap<String, String>(2); + public void parsePathIsOff() throws Exception { + this.handler.setParsePath(false); + Map<String, String> vars = new HashMap<>(2); vars.put("hotel", "1"); vars.put("publicpath", "pics/logo.png"); String template = "http://example.com/hotels/{hotel}/pic/{publicpath}"; - URI actual = this.handler.expand(template, vars); - URI expected = new URI("http://example.com/hotels/1/pic/pics/logo.png"); - assertEquals(expected, actual); + assertEquals("http://example.com/hotels/1/pic/pics/logo.png", actual.toString()); } @Test - public void expandWithFullPathAndParsePathEnabled() throws Exception { - Map<String, String> vars = new HashMap<String, String>(2); + public void parsePathIsOn() throws Exception { + this.handler.setParsePath(true); + Map<String, String> vars = new HashMap<>(2); vars.put("hotel", "1"); vars.put("publicpath", "pics/logo.png"); vars.put("scale", "150x150"); String template = "http://example.com/hotels/{hotel}/pic/{publicpath}/size/{scale}"; + URI actual = this.handler.expand(template, vars); - this.handler.setParsePath(true); + assertEquals("http://example.com/hotels/1/pic/pics%2Flogo.png/size/150x150", actual.toString()); + } + + @Test + public void strictEncodingIsOffWithMap() throws Exception { + this.handler.setStrictEncoding(false); + Map<String, String> vars = new HashMap<>(2); + vars.put("userId", "john;doe"); + String template = "http://www.example.com/user/{userId}/dashboard"; + URI actual = this.handler.expand(template, vars); + + assertEquals("http://www.example.com/user/john;doe/dashboard", actual.toString()); + } + + @Test + public void strictEncodingOffWithArray() throws Exception { + this.handler.setStrictEncoding(false); + String template = "http://www.example.com/user/{userId}/dashboard"; + URI actual = this.handler.expand(template, "john;doe"); + + assertEquals("http://www.example.com/user/john;doe/dashboard", actual.toString()); + } + + @Test + public void strictEncodingOnWithMap() throws Exception { + this.handler.setStrictEncoding(true); + Map<String, String> vars = new HashMap<>(2); + vars.put("userId", "john;doe"); + String template = "http://www.example.com/user/{userId}/dashboard"; + URI actual = this.handler.expand(template, vars); + + assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", actual.toString()); + } + + @Test + public void strictEncodingOnWithArray() throws Exception { + this.handler.setStrictEncoding(true); + String template = "http://www.example.com/user/{userId}/dashboard"; + URI actual = this.handler.expand(template, "john;doe"); + + assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", actual.toString()); + } + + @Test // SPR-14147 + public void strictEncodingAndDefaultUriVariables() throws Exception { + Map<String, String> defaultVars = new HashMap<>(1); + defaultVars.put("host", "www.example.com"); + this.handler.setDefaultUriVariables(defaultVars); + this.handler.setStrictEncoding(true); + + Map<String, Object> vars = new HashMap<>(1); + vars.put("userId", "john;doe"); + + String template = "http://{host}/user/{userId}/dashboard"; URI actual = this.handler.expand(template, vars); - URI expected = new URI("http://example.com/hotels/1/pic/pics%2Flogo.png/size/150x150"); - assertEquals(expected, actual); + assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", actual.toString()); } } diff --git a/spring-web/src/test/java/org/springframework/web/util/Log4jWebConfigurerTests.java b/spring-web/src/test/java/org/springframework/web/util/Log4jWebConfigurerTests.java index 5001f9fb..c32405e4 100644 --- a/spring-web/src/test/java/org/springframework/web/util/Log4jWebConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/Log4jWebConfigurerTests.java @@ -101,7 +101,8 @@ public class Log4jWebConfigurerTests { try { assertLogOutput(); - } finally { + } + finally { Log4jWebConfigurer.shutdownLogging(sc); } assertTrue(MockLog4jAppender.closeCalled); @@ -132,7 +133,8 @@ public class Log4jWebConfigurerTests { try { assertLogOutput(); - } finally { + } + finally { listener.contextDestroyed(new ServletContextEvent(sc)); } assertTrue(MockLog4jAppender.closeCalled); diff --git a/spring-web/src/test/java/org/springframework/web/util/UriTemplateTests.java b/spring-web/src/test/java/org/springframework/web/util/UriTemplateTests.java index a047af03..99c642f0 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriTemplateTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,116 +30,126 @@ import static org.junit.Assert.*; /** * @author Arjen Poutsma * @author Juergen Hoeller + * @author Rossen Stoyanchev */ public class UriTemplateTests { @Test public void getVariableNames() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); List<String> variableNames = template.getVariableNames(); assertEquals("Invalid variable names", Arrays.asList("hotel", "booking"), variableNames); } @Test public void expandVarArgs() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); URI result = template.expand("1", "42"); - assertEquals("Invalid expanded template", new URI("http://example.com/hotels/1/bookings/42"), result); + assertEquals("Invalid expanded template", new URI("/hotels/1/bookings/42"), result); + } + + // SPR-9712 + + @Test + public void expandVarArgsWithArrayValue() throws Exception { + UriTemplate template = new UriTemplate("/sum?numbers={numbers}"); + URI result = template.expand(new int[] {1, 2, 3}); + assertEquals(new URI("/sum?numbers=1,2,3"), result); } @Test(expected = IllegalArgumentException.class) public void expandVarArgsNotEnoughVariables() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); template.expand("1"); } @Test public void expandMap() throws Exception { - Map<String, String> uriVariables = new HashMap<String, String>(2); + Map<String, String> uriVariables = new HashMap<>(2); uriVariables.put("booking", "42"); uriVariables.put("hotel", "1"); - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); URI result = template.expand(uriVariables); - assertEquals("Invalid expanded template", new URI("http://example.com/hotels/1/bookings/42"), result); + assertEquals("Invalid expanded template", new URI("/hotels/1/bookings/42"), result); } @Test public void expandMapDuplicateVariables() throws Exception { UriTemplate template = new UriTemplate("/order/{c}/{c}/{c}"); - assertEquals("Invalid variable names", Arrays.asList("c", "c", "c"), template.getVariableNames()); + assertEquals(Arrays.asList("c", "c", "c"), template.getVariableNames()); URI result = template.expand(Collections.singletonMap("c", "cheeseburger")); - assertEquals("Invalid expanded template", new URI("/order/cheeseburger/cheeseburger/cheeseburger"), result); + assertEquals(new URI("/order/cheeseburger/cheeseburger/cheeseburger"), result); } @Test public void expandMapNonString() throws Exception { - Map<String, Integer> uriVariables = new HashMap<String, Integer>(2); + Map<String, Integer> uriVariables = new HashMap<>(2); uriVariables.put("booking", 42); uriVariables.put("hotel", 1); - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); URI result = template.expand(uriVariables); - assertEquals("Invalid expanded template", new URI("http://example.com/hotels/1/bookings/42"), result); + assertEquals("Invalid expanded template", new URI("/hotels/1/bookings/42"), result); } @Test public void expandMapEncoded() throws Exception { Map<String, String> uriVariables = Collections.singletonMap("hotel", "Z\u00fcrich"); - UriTemplate template = new UriTemplate("http://example.com/hotel list/{hotel}"); + UriTemplate template = new UriTemplate("/hotel list/{hotel}"); URI result = template.expand(uriVariables); - assertEquals("Invalid expanded template", new URI("http://example.com/hotel%20list/Z%C3%BCrich"), result); + assertEquals("Invalid expanded template", new URI("/hotel%20list/Z%C3%BCrich"), result); } @Test(expected = IllegalArgumentException.class) public void expandMapUnboundVariables() throws Exception { - Map<String, String> uriVariables = new HashMap<String, String>(2); + Map<String, String> uriVariables = new HashMap<>(2); uriVariables.put("booking", "42"); uriVariables.put("bar", "1"); - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); template.expand(uriVariables); } @Test public void expandEncoded() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotel list/{hotel}"); + UriTemplate template = new UriTemplate("/hotel list/{hotel}"); URI result = template.expand("Z\u00fcrich"); - assertEquals("Invalid expanded template", new URI("http://example.com/hotel%20list/Z%C3%BCrich"), result); + assertEquals("Invalid expanded template", new URI("/hotel%20list/Z%C3%BCrich"), result); } @Test public void matches() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); - assertTrue("UriTemplate does not match", template.matches("http://example.com/hotels/1/bookings/42")); - assertFalse("UriTemplate matches", template.matches("http://example.com/hotels/bookings")); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); + assertTrue("UriTemplate does not match", template.matches("/hotels/1/bookings/42")); + assertFalse("UriTemplate matches", template.matches("/hotels/bookings")); assertFalse("UriTemplate matches", template.matches("")); assertFalse("UriTemplate matches", template.matches(null)); } @Test public void matchesCustomRegex() throws Exception { - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel:\\d+}"); - assertTrue("UriTemplate does not match", template.matches("http://example.com/hotels/42")); - assertFalse("UriTemplate matches", template.matches("http://example.com/hotels/foo")); + UriTemplate template = new UriTemplate("/hotels/{hotel:\\d+}"); + assertTrue("UriTemplate does not match", template.matches("/hotels/42")); + assertFalse("UriTemplate matches", template.matches("/hotels/foo")); } @Test public void match() throws Exception { - Map<String, String> expected = new HashMap<String, String>(2); + Map<String, String> expected = new HashMap<>(2); expected.put("booking", "42"); expected.put("hotel", "1"); - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); - Map<String, String> result = template.match("http://example.com/hotels/1/bookings/42"); + UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}"); + Map<String, String> result = template.match("/hotels/1/bookings/42"); assertEquals("Invalid match", expected, result); } @Test public void matchCustomRegex() throws Exception { - Map<String, String> expected = new HashMap<String, String>(2); + Map<String, String> expected = new HashMap<>(2); expected.put("booking", "42"); expected.put("hotel", "1"); - UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel:\\d}/bookings/{booking:\\d+}"); - Map<String, String> result = template.match("http://example.com/hotels/1/bookings/42"); + UriTemplate template = new UriTemplate("/hotels/{hotel:\\d}/bookings/{booking:\\d+}"); + Map<String, String> result = template.match("/hotels/1/bookings/42"); assertEquals("Invalid match", expected, result); } @@ -164,7 +174,7 @@ public class UriTemplateTests { public void matchMultipleInOneSegment() throws Exception { UriTemplate template = new UriTemplate("/{foo}-{bar}"); Map<String, String> result = template.match("/12-34"); - Map<String, String> expected = new HashMap<String, String>(2); + Map<String, String> expected = new HashMap<>(2); expected.put("foo", "12"); expected.put("bar", "34"); assertEquals("Invalid match", expected, result); diff --git a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java index 6480c5fa..2227674a 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -24,6 +24,7 @@ import static org.junit.Assert.*; /** * @author Arjen Poutsma + * @author Juergen Hoeller */ public class UriUtilsTests { @@ -104,4 +105,22 @@ public class UriUtilsTests { UriUtils.decode("foo%2", ENC); } + @Test + public void extractFileExtension() { + assertEquals("html", UriUtils.extractFileExtension("index.html")); + assertEquals("html", UriUtils.extractFileExtension("/index.html")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a#/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a.do#/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html;r=22?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html;r=22;s=33?param=/path/a.do")); + } + } |