From ea88bc2c816cddd21df90119361b75ad8c566b46 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 16 May 2014 18:06:29 +0200 Subject: [PATCH] Servlet/PortletResponse supported as a resolvable dependency now (in particular for web controllers) This feature required support for response exposure on Servlet/PortletRequestAttributes, instead of just in the Servlet/PortletWebRequest subclasses. Issue: SPR-11795 --- .../request/ServletRequestAttributes.java | 22 +++++++++++++- .../context/request/ServletWebRequest.java | 30 +++++++------------ .../support/WebApplicationContextUtils.java | 30 +++++++++++++++++-- .../web/filter/RequestContextFilter.java | 5 ++-- .../web/portlet/FrameworkPortlet.java | 4 +-- .../PortletApplicationContextUtils.java | 29 ++++++++++++++++-- .../context/PortletRequestAttributes.java | 22 +++++++++++++- .../portlet/context/PortletWebRequest.java | 15 ++-------- .../Portlet20AnnotationControllerTests.java | 18 ++++++----- .../web/servlet/FrameworkServlet.java | 4 +-- .../ServletAnnotationControllerTests.java | 26 +++++++--------- 11 files changed, 138 insertions(+), 67 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java index 5dbfd873b26..8ad2fdb9979 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -19,6 +19,7 @@ package org.springframework.web.context.request; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.util.Assert; @@ -48,6 +49,8 @@ public class ServletRequestAttributes extends AbstractRequestAttributes { private final HttpServletRequest request; + private HttpServletResponse response; + private volatile HttpSession session; private final Map sessionAttributesToUpdate = new ConcurrentHashMap(1); @@ -62,6 +65,16 @@ public class ServletRequestAttributes extends AbstractRequestAttributes { this.request = request; } + /** + * Create a new ServletRequestAttributes instance for the given request. + * @param request current HTTP request + * @param response current HTTP response (for optional exposure) + */ + public ServletRequestAttributes(HttpServletRequest request, HttpServletResponse response) { + this(request); + this.response = response; + } + /** * Exposes the native {@link HttpServletRequest} that we're wrapping. @@ -70,6 +83,13 @@ public class ServletRequestAttributes extends AbstractRequestAttributes { return this.request; } + /** + * Exposes the native {@link HttpServletResponse} that we're wrapping (if any). + */ + public final HttpServletResponse getResponse() { + return this.response; + } + /** * Exposes the {@link HttpSession} that we're wrapping. * @param allowCreate whether to allow creation of a new session if none exists yet diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index d41b258b800..079314f606c 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -52,8 +52,6 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ private static final String METHOD_HEAD = "HEAD"; - private HttpServletResponse response; - private boolean notModified = false; @@ -71,18 +69,10 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ * @param response current HTTP response (for automatic last-modified handling) */ public ServletWebRequest(HttpServletRequest request, HttpServletResponse response) { - this(request); - this.response = response; + super(request, response); } - /** - * Exposes the native {@link HttpServletRequest} that we're wrapping (if any). - */ - public final HttpServletResponse getResponse() { - return this.response; - } - @Override public Object getNativeRequest() { return getRequest(); @@ -181,8 +171,9 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ @Override @SuppressWarnings("deprecation") public boolean checkNotModified(long lastModifiedTimestamp) { + HttpServletResponse response = getResponse(); if (lastModifiedTimestamp >= 0 && !this.notModified && - (this.response == null || !this.response.containsHeader(HEADER_LAST_MODIFIED))) { + (response == null || !response.containsHeader(HEADER_LAST_MODIFIED))) { long ifModifiedSince = -1; try { ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE); @@ -202,12 +193,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ } } this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000)); - if (this.response != null) { + if (response != null) { if (this.notModified && supportsNotModifiedStatus()) { - this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { - this.response.setDateHeader(HEADER_LAST_MODIFIED, lastModifiedTimestamp); + response.setDateHeader(HEADER_LAST_MODIFIED, lastModifiedTimestamp); } } } @@ -216,16 +207,17 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ @Override public boolean checkNotModified(String etag) { + HttpServletResponse response = getResponse(); if (StringUtils.hasLength(etag) && !this.notModified && - (this.response == null || !this.response.containsHeader(HEADER_ETAG))) { + (response == null || !response.containsHeader(HEADER_ETAG))) { String ifNoneMatch = getRequest().getHeader(HEADER_IF_NONE_MATCH); this.notModified = etag.equals(ifNoneMatch); - if (this.response != null) { + if (response != null) { if (this.notModified && supportsNotModifiedStatus()) { - this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { - this.response.setHeader(HEADER_ETAG, etag); + response.setHeader(HEADER_ETAG, etag); } } } diff --git a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java index a3e7ffb5dcb..589583b3e12 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -26,6 +26,7 @@ import javax.faces.context.FacesContext; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.ObjectFactory; @@ -154,6 +155,7 @@ public abstract class WebApplicationContextUtils { } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); + beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); if (jsfPresent) { @@ -300,6 +302,29 @@ public abstract class WebApplicationContextUtils { } + /** + * Factory that exposes the current response object on demand. + */ + @SuppressWarnings("serial") + private static class ResponseObjectFactory implements ObjectFactory, Serializable { + + @Override + public ServletResponse getObject() { + ServletResponse response = currentRequestAttributes().getResponse(); + if (response == null) { + throw new IllegalStateException("Current servlet response not available - " + + "consider using RequestContextFilter instead of RequestContextListener"); + } + return response; + } + + @Override + public String toString() { + return "Current HttpServletResponse"; + } + } + + /** * Factory that exposes the current session object on demand. */ @@ -326,7 +351,8 @@ public abstract class WebApplicationContextUtils { @Override public WebRequest getObject() { - return new ServletWebRequest(currentRequestAttributes().getRequest()); + ServletRequestAttributes requestAttr = currentRequestAttributes(); + return new ServletWebRequest(requestAttr.getRequest(), requestAttr.getResponse()); } @Override diff --git a/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java b/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java index 5a1f3b5dd22..173083d56c3 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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,7 +17,6 @@ package org.springframework.web.filter; import java.io.IOException; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -92,7 +91,7 @@ public class RequestContextFilter extends OncePerRequestFilter { HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - ServletRequestAttributes attributes = new ServletRequestAttributes(request); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); initContextHolders(request, attributes); try { diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java index d2c028596b8..29f882ef671 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -507,7 +507,7 @@ public abstract class FrameworkPortlet extends GenericPortletBean RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); PortletRequestAttributes requestAttributes = null; if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(PortletRequestAttributes.class)) { - requestAttributes = new PortletRequestAttributes(request); + requestAttributes = new PortletRequestAttributes(request, response); RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java index 31c5affaa10..1973d8f8c20 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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 java.util.Map; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.servlet.ServletContext; @@ -125,6 +126,7 @@ public abstract class PortletApplicationContextUtils { } beanFactory.registerResolvableDependency(PortletRequest.class, new RequestObjectFactory()); + beanFactory.registerResolvableDependency(PortletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(PortletSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); } @@ -255,6 +257,28 @@ public abstract class PortletApplicationContextUtils { } + /** + * Factory that exposes the current response object on demand. + */ + @SuppressWarnings("serial") + private static class ResponseObjectFactory implements ObjectFactory, Serializable { + + @Override + public PortletResponse getObject() { + PortletResponse response = currentRequestAttributes().getResponse(); + if (response == null) { + throw new IllegalStateException("Current portlet response not available"); + } + return response; + } + + @Override + public String toString() { + return "Current PortletResponse"; + } + } + + /** * Factory that exposes the current session object on demand. */ @@ -281,7 +305,8 @@ public abstract class PortletApplicationContextUtils { @Override public WebRequest getObject() { - return new PortletWebRequest(currentRequestAttributes().getRequest()); + PortletRequestAttributes requestAttr = currentRequestAttributes(); + return new PortletWebRequest(requestAttr.getRequest(), requestAttr.getResponse()); } @Override diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletRequestAttributes.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletRequestAttributes.java index d43ffbb6ae0..c1f5b1eb83f 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletRequestAttributes.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletRequestAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -19,6 +19,7 @@ package org.springframework.web.portlet.context; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import org.springframework.util.Assert; @@ -57,6 +58,8 @@ public class PortletRequestAttributes extends AbstractRequestAttributes { private final PortletRequest request; + private PortletResponse response; + private volatile PortletSession session; private final Map sessionAttributesToUpdate = new ConcurrentHashMap(1); @@ -73,6 +76,16 @@ public class PortletRequestAttributes extends AbstractRequestAttributes { this.request = request; } + /** + * Create a new PortletRequestAttributes instance for the given request. + * @param request current portlet request + * @param response current portlet response (for optional exposure) + */ + public PortletRequestAttributes(PortletRequest request, PortletResponse response) { + this(request); + this.response = response; + } + /** * Exposes the native {@link PortletRequest} that we're wrapping. @@ -81,6 +94,13 @@ public class PortletRequestAttributes extends AbstractRequestAttributes { return this.request; } + /** + * Exposes the native {@link PortletResponse} that we're wrapping (if any). + */ + public final PortletResponse getResponse() { + return this.response; + } + /** * Exposes the {@link PortletSession} that we're wrapping. * @param allowCreate whether to allow creation of a new session if none exists yet diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java index 90529bf79b1..29b65b765de 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -39,9 +39,6 @@ import org.springframework.web.portlet.util.PortletUtils; */ public class PortletWebRequest extends PortletRequestAttributes implements NativeWebRequest { - private PortletResponse response; - - /** * Create a new PortletWebRequest instance for the given request. * @param request current portlet request @@ -56,18 +53,10 @@ public class PortletWebRequest extends PortletRequestAttributes implements Nativ * @param response current portlet response */ public PortletWebRequest(PortletRequest request, PortletResponse response) { - this(request); - this.response = response; + super(request, response); } - /** - * Exposes the native {@link PortletResponse} that we're wrapping (if any). - */ - public final PortletResponse getResponse() { - return this.response; - } - @Override public Object getNativeRequest() { return getRequest(); diff --git a/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java b/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java index 755738871e0..3832a271a0e 100644 --- a/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java +++ b/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/mvc/annotation/Portlet20AnnotationControllerTests.java @@ -23,7 +23,6 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; - import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.EventResponse; @@ -31,6 +30,7 @@ import javax.portlet.MimeResponse; import javax.portlet.PortletContext; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; @@ -42,9 +42,6 @@ import javax.servlet.http.Cookie; import org.junit.Test; import org.springframework.beans.BeansException; -import org.springframework.tests.sample.beans.DerivedTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.propertyeditors.CustomDateEditor; @@ -63,6 +60,9 @@ import org.springframework.mock.web.portlet.MockRenderResponse; import org.springframework.mock.web.portlet.MockResourceRequest; import org.springframework.mock.web.portlet.MockResourceResponse; import org.springframework.stereotype.Controller; +import org.springframework.tests.sample.beans.DerivedTestBean; +import org.springframework.tests.sample.beans.ITestBean; +import org.springframework.tests.sample.beans.TestBean; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; @@ -1088,17 +1088,21 @@ public class Portlet20AnnotationControllerTests { private PortletContext portletContext; @Autowired - private PortletSession session; + private PortletRequest request; @Autowired - private PortletRequest request; + private PortletResponse response; + + @Autowired + private PortletSession session; @Autowired private WebRequest webRequest; @RenderMapping public void myHandle(RenderResponse response) throws IOException { - if (this.portletContext == null || this.session == null || this.request == null || this.webRequest == null) { + if (this.portletContext == null || this.request == null || this.response == null || + this.session == null || this.webRequest == null) { throw new IllegalStateException(); } response.getWriter().write("myView"); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 1a36b0a7c04..abfbe4e5c5f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -1023,7 +1023,7 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) { if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) { - return new ServletRequestAttributes(request); + return new ServletRequestAttributes(request, response); } else { return null; // preserve the pre-bound RequestAttributes instance diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index ff5fa87232c..d0b1bfcdd73 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -16,16 +16,6 @@ package org.springframework.web.servlet.mvc.annotation; -import static org.hamcrest.Matchers.instanceOf; -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.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.beans.PropertyEditorSupport; import java.io.IOException; import java.io.Serializable; @@ -53,7 +43,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; - import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -65,6 +54,7 @@ import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlRootElement; import org.junit.Test; + import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.support.DefaultPointcutAdvisor; @@ -150,6 +140,9 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.util.NestedServletException; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + /** * @author Juergen Hoeller * @author Sam Brannen @@ -2483,18 +2476,21 @@ public class ServletAnnotationControllerTests { private transient ServletConfig servletConfig; @Autowired - private HttpSession session; + private HttpServletRequest request; @Autowired - private HttpServletRequest request; + private HttpServletResponse response; + + @Autowired + private HttpSession session; @Autowired private WebRequest webRequest; @RequestMapping public void myHandle(HttpServletResponse response, HttpServletRequest request) throws IOException { - if (this.servletContext == null || this.servletConfig == null || this.session == null || - this.request == null || this.webRequest == null) { + if (this.servletContext == null || this.servletConfig == null || this.request == null || + this.response == null || this.session == null || this.webRequest == null) { throw new IllegalStateException(); } response.getWriter().write("myView");