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
This commit is contained in:
Juergen Hoeller 2014-05-16 18:06:29 +02:00
parent 5faacd5a3d
commit ea88bc2c81
11 changed files with 138 additions and 67 deletions

View File

@ -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<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<String, Object>(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

View File

@ -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);
}
}
}

View File

@ -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<ServletResponse>, 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

View File

@ -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 {

View File

@ -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);
}

View File

@ -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<PortletResponse>, 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

View File

@ -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<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<String, Object>(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

View File

@ -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();

View File

@ -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");

View File

@ -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

View File

@ -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");