diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 38c0b49f27..607b02a66c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -714,8 +714,22 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { + ModelAndView mav = null; checkRequest(request); + // Execute invokeHandlerMethod in synchronized block if required. + if (this.synchronizeOnSession) { + HttpSession session = request.getSession(false); + if (session != null) { + Object mutex = WebUtils.getSessionMutex(session); + synchronized (mutex) { + mav = invokeHandlerMethod(request, response, handlerMethod); + } + } + } + + mav = invokeHandlerMethod(request, response, handlerMethod); + if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } @@ -723,18 +737,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter prepareResponse(response); } - // Execute invokeHandlerMethod in synchronized block if required. - if (this.synchronizeOnSession) { - HttpSession session = request.getSession(false); - if (session != null) { - Object mutex = WebUtils.getSessionMutex(session); - synchronized (mutex) { - return invokeHandlerMethod(request, response, handlerMethod); - } - } - } - - return invokeHandlerMethod(request, response, handlerMethod); + return mav; } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index fda8819180..8bf68bba4a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.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,6 +20,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -329,14 +330,16 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { * @since 4.2 */ protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) { - String ccValue = cacheControl.getHeaderValue(); - if (ccValue != null) { - // Set computed HTTP 1.1 Cache-Control header - response.setHeader(HEADER_CACHE_CONTROL, ccValue); + if (!response.containsHeader(HEADER_CACHE_CONTROL)) { + String ccValue = cacheControl.getHeaderValue(); + if (ccValue != null) { + // Set computed HTTP 1.1 Cache-Control header + response.setHeader(HEADER_CACHE_CONTROL, ccValue); - if (response.containsHeader(HEADER_PRAGMA)) { - // Reset HTTP 1.0 Pragma header if present - response.setHeader(HEADER_PRAGMA, ""); + if (response.containsHeader(HEADER_PRAGMA)) { + // Reset HTTP 1.0 Pragma header if present + response.setHeader(HEADER_PRAGMA, ""); + } } } } @@ -352,30 +355,32 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { */ @SuppressWarnings("deprecation") protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) { - if (this.useExpiresHeader || !this.useCacheControlHeader) { - // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions - if (cacheSeconds > 0) { - cacheForSeconds(response, cacheSeconds); - } - else if (cacheSeconds == 0) { - preventCaching(response); - } - } - else { - CacheControl cControl; - if (cacheSeconds > 0) { - cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); - if (this.alwaysMustRevalidate) { - cControl = cControl.mustRevalidate(); + if (!response.containsHeader(HEADER_CACHE_CONTROL)) { + if (this.useExpiresHeader || !this.useCacheControlHeader) { + // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions + if (cacheSeconds > 0) { + cacheForSeconds(response, cacheSeconds); + } + else if (cacheSeconds == 0) { + preventCaching(response); } } - else if (cacheSeconds == 0) { - cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); - } else { - cControl = CacheControl.empty(); + CacheControl cControl; + if (cacheSeconds > 0) { + cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); + if (this.alwaysMustRevalidate) { + cControl = cControl.mustRevalidate(); + } + } + else if (cacheSeconds == 0) { + cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); + } + else { + cControl = CacheControl.empty(); + } + applyCacheControl(response, cControl); } - applyCacheControl(response, cControl); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java index 3ab23141c9..605b129a7b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.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. @@ -49,7 +49,7 @@ public class ControllerTests { ParameterizableViewController pvc = new ParameterizableViewController(); pvc.setViewName(viewName); // We don't care about the params. - ModelAndView mv = pvc.handleRequest(new MockHttpServletRequest("GET", "foo.html"), null); + ModelAndView mv = pvc.handleRequest(new MockHttpServletRequest("GET", "foo.html"), new MockHttpServletResponse()); assertTrue("model has no data", mv.getModel().size() == 0); assertTrue("model has correct viewname", mv.getViewName().equals(viewName)); assertTrue("getViewName matches", pvc.getViewName().equals(viewName)); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java index 6ef5cb0188..5ba4bb5499 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.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. @@ -28,11 +28,14 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; + import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -40,8 +43,8 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.core.MethodParameter; +import org.springframework.http.CacheControl; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.test.MockHttpServletRequest; @@ -85,6 +88,7 @@ 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.assertThat; import static org.junit.Assert.assertTrue; /** @@ -262,6 +266,24 @@ public class RequestMappingHandlerAdapterIntegrationTests { assertEquals(HttpStatus.ACCEPTED.value(), response.getStatus()); assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8")); assertEquals("headerValue", response.getHeader("header")); + // set because of @SesstionAttributes + assertEquals("no-store", response.getHeader("Cache-Control")); + } + + // SPR-13867 + @Test + public void handleHttpEntityWithCacheControl() throws Exception { + Class[] parameterTypes = new Class[] { HttpEntity.class }; + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.setContent("Hello Server".getBytes("UTF-8")); + + HandlerMethod handlerMethod = handlerMethod("handleHttpEntityWithCacheControl", parameterTypes); + ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod); + + assertNull(mav); + assertEquals(HttpStatus.OK.value(), response.getStatus()); + assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8")); + assertThat(response.getHeaderValues("Cache-Control"), Matchers.contains("max-age=3600")); } @Test @@ -373,10 +395,15 @@ public class RequestMappingHandlerAdapterIntegrationTests { } public ResponseEntity handleHttpEntity(HttpEntity httpEntity) throws Exception { - HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.set("header", "headerValue"); String responseBody = "Handled requestBody=[" + new String(httpEntity.getBody(), "UTF-8") + "]"; - return new ResponseEntity(responseBody, responseHeaders, HttpStatus.ACCEPTED); + return ResponseEntity.accepted() + .header("header", "headerValue") + .body(responseBody); + } + + public ResponseEntity handleHttpEntityWithCacheControl(HttpEntity httpEntity) throws Exception { + String responseBody = "Handled requestBody=[" + new String(httpEntity.getBody(), "UTF-8") + "]"; + return ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS)).body(responseBody); } public void handleRequestPart(@RequestPart String requestPart, Model model) {