From fd97c8d7a4d4717465c3bb09f2a17f7e2d56e973 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 21 Oct 2011 22:16:08 +0000 Subject: [PATCH] SPR-8770 Ensure RequestDataValueProcessor is invoked from RedirectView. --- .../web/servlet/view/RedirectView.java | 39 ++++++++-- .../servlet/view/UrlBasedViewResolver.java | 9 ++- .../RequestDataValueProcessorWrapper.java} | 4 +- .../form/AbstractHtmlElementTagTests.java | 5 +- .../ContentNegotiatingViewResolverTests.java | 33 ++++++--- .../web/servlet/view/RedirectViewTests.java | 71 ++++++++++++++++++- .../web/servlet/view/ViewResolverTests.java | 1 + 7 files changed, 135 insertions(+), 27 deletions(-) rename org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/{tags/form/DelegatingRequestDataValueProcessor.java => support/RequestDataValueProcessorWrapper.java} (92%) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/RedirectView.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/RedirectView.java index 87aba51ea0..184c9a3284 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/RedirectView.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/RedirectView.java @@ -38,6 +38,8 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.SmartView; @@ -250,13 +252,7 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView { String targetUrl = createTargetUrl(model, request); - if (getWebApplicationContext() != null) { - RequestContext requestContext = createRequestContext(request, response, model); - RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor(); - if (processor != null) { - targetUrl = processor.processUrl(request, targetUrl); - } - } + targetUrl = updateTargetUrl(targetUrl, model, request, response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); if (!CollectionUtils.isEmpty(flashMap)) { @@ -489,6 +485,35 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView { return (input != null ? URLEncoder.encode(input, encodingScheme) : null); } + /** + * Find the registered {@link RequestDataValueProcessor}, if any, and allow + * it to update the redirect target URL. + * @return the updated URL or the same as URL as the one passed in + */ + protected String updateTargetUrl(String targetUrl, Map model, + HttpServletRequest request, HttpServletResponse response) { + + RequestContext requestContext = null; + if (getWebApplicationContext() != null) { + requestContext = createRequestContext(request, response, model); + } + else { + WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext(); + if (wac != null && wac.getServletContext() != null) { + requestContext = new RequestContext(request, response, wac.getServletContext(), model); + } + } + + if (requestContext != null) { + RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor(); + if (processor != null) { + targetUrl = processor.processUrl(request, targetUrl); + } + } + + return targetUrl; + } + /** * Send a redirect back to the HTTP client * @param request current HTTP request (allows for reacting to request method) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/UrlBasedViewResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/UrlBasedViewResolver.java index 3c6fcf50ef..b9a6b6bf11 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/UrlBasedViewResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/UrlBasedViewResolver.java @@ -389,7 +389,8 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements // Check for special "redirect:" prefix. if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); - return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); + RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); + return applyLifecycleMethods(viewName, view); } // Check for special "forward:" prefix. if (viewName.startsWith(FORWARD_URL_PREFIX)) { @@ -433,10 +434,14 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements @Override protected View loadView(String viewName, Locale locale) throws Exception { AbstractUrlBasedView view = buildView(viewName); - View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName); + View result = applyLifecycleMethods(viewName, view); return (view.checkResource(locale) ? result : null); } + private View applyLifecycleMethods(String viewName, AbstractView view) { + return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName); + } + /** * Creates a new View instance of the specified view class and configures it. * Does not perform any lookup for pre-defined View instances. diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/DelegatingRequestDataValueProcessor.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/RequestDataValueProcessorWrapper.java similarity index 92% rename from org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/DelegatingRequestDataValueProcessor.java rename to org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/RequestDataValueProcessorWrapper.java index 80745e02c0..d26f18d120 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/DelegatingRequestDataValueProcessor.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/RequestDataValueProcessorWrapper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.servlet.tags.form; +package org.springframework.web.servlet.support; import java.util.Map; @@ -22,7 +22,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.web.servlet.support.RequestDataValueProcessor; -class DelegatingRequestDataValueProcessor implements RequestDataValueProcessor { +public class RequestDataValueProcessorWrapper implements RequestDataValueProcessor { private RequestDataValueProcessor processor; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTagTests.java index 24474e4600..d51357a638 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTagTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTagTests.java @@ -32,6 +32,7 @@ import org.springframework.mock.web.MockPageContext; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.context.support.StaticWebApplicationContext; +import org.springframework.web.servlet.support.RequestDataValueProcessorWrapper; import org.springframework.web.servlet.support.JspAwareRequestContext; import org.springframework.web.servlet.support.RequestContext; import org.springframework.web.servlet.support.RequestContextUtils; @@ -66,7 +67,7 @@ public abstract class AbstractHtmlElementTagTests extends AbstractTagTests { MockPageContext pageContext = createPageContext(); MockHttpServletRequest request = (MockHttpServletRequest) pageContext.getRequest(); StaticWebApplicationContext wac = (StaticWebApplicationContext) RequestContextUtils.getWebApplicationContext(request); - wac.registerSingleton("requestDataValueProcessor", DelegatingRequestDataValueProcessor.class); + wac.registerSingleton("requestDataValueProcessor", RequestDataValueProcessorWrapper.class); extendRequest(request); extendPageContext(pageContext); RequestContext requestContext = new JspAwareRequestContext(pageContext); @@ -103,7 +104,7 @@ public abstract class AbstractHtmlElementTagTests extends AbstractTagTests { RequestDataValueProcessor mockProcessor = createMock(RequestDataValueProcessor.class); ServletRequest request = getPageContext().getRequest(); StaticWebApplicationContext wac = (StaticWebApplicationContext) RequestContextUtils.getWebApplicationContext(request); - wac.getBean(DelegatingRequestDataValueProcessor.class).setRequestDataValueProcessor(mockProcessor); + wac.getBean(RequestDataValueProcessorWrapper.class).setRequestDataValueProcessor(mockProcessor); return mockProcessor; } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java index 70bf0627cf..d2e431d52a 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java @@ -16,6 +16,16 @@ package org.springframework.web.servlet.view; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +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 java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -28,7 +38,6 @@ import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; - import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -40,9 +49,6 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; - /** * @author Arjen Poutsma */ @@ -418,8 +424,14 @@ public class ContentNegotiatingViewResolverTests { request.addHeader("Accept", "application/json"); request.setRequestURI("/test"); + StaticWebApplicationContext webAppContext = new StaticWebApplicationContext(); + webAppContext.setServletContext(new MockServletContext()); + webAppContext.refresh(); + + UrlBasedViewResolver urlViewResolver = new InternalResourceViewResolver(); + urlViewResolver.setApplicationContext(webAppContext); ViewResolver xmlViewResolver = createMock(ViewResolver.class); - viewResolver.setViewResolvers(Arrays.asList(xmlViewResolver, new UrlBasedViewResolver())); + viewResolver.setViewResolvers(Arrays.asList(xmlViewResolver, urlViewResolver)); View xmlView = createMock("application_xml", View.class); View jsonView = createMock("application_json", View.class); @@ -491,15 +503,14 @@ public class ContentNegotiatingViewResolverTests { @Test public void nestedViewResolverIsNotSpringBean() throws Exception { + StaticWebApplicationContext webAppContext = new StaticWebApplicationContext(); + webAppContext.setServletContext(new MockServletContext()); + webAppContext.refresh(); + InternalResourceViewResolver nestedResolver = new InternalResourceViewResolver(); + nestedResolver.setApplicationContext(webAppContext); nestedResolver.setViewClass(InternalResourceView.class); viewResolver.setViewResolvers(new ArrayList(Arrays.asList(nestedResolver))); - - StaticWebApplicationContext appContext = new StaticWebApplicationContext(); - appContext.setServletContext(new MockServletContext()); - appContext.refresh(); - viewResolver.setApplicationContext(appContext); - viewResolver.setDefaultContentType(MediaType.TEXT_HTML); String viewName = "view"; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/RedirectViewTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/RedirectViewTests.java index 4f74c5c392..5f80b80e63 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/RedirectViewTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/RedirectViewTests.java @@ -34,15 +34,22 @@ import javax.servlet.http.HttpServletResponse; import junit.framework.AssertionFailedError; +import org.easymock.EasyMock; import org.junit.Test; import org.springframework.beans.TestBean; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; import org.springframework.ui.ModelMap; +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.support.StaticWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.View; +import org.springframework.web.servlet.support.RequestDataValueProcessorWrapper; +import org.springframework.web.servlet.support.RequestDataValueProcessor; import org.springframework.web.util.WebUtils; /** @@ -118,8 +125,8 @@ public class RedirectViewTests { RedirectView rv = new RedirectView(); rv.setUrl("http://url.somewhere.com/path"); rv.setHttp10Compatible(false); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); + HttpServletRequest request = new MockHttpServletRequest(); + HttpServletResponse response = new MockHttpServletResponse(); FlashMap flashMap = new FlashMap(); flashMap.put("successMessage", "yay!"); request.setAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap); @@ -131,6 +138,64 @@ public class RedirectViewTests { assertEquals("/path", flashMap.getTargetRequestPath()); assertEquals(model, flashMap.getTargetRequestParams().toSingleValueMap()); } + + @Test + public void updateTargetUrl() throws Exception { + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.registerSingleton("requestDataValueProcessor", RequestDataValueProcessorWrapper.class); + wac.setServletContext(new MockServletContext()); + wac.refresh(); + + RequestDataValueProcessor mockProcessor = createMock(RequestDataValueProcessor.class); + wac.getBean(RequestDataValueProcessorWrapper.class).setRequestDataValueProcessor(mockProcessor); + + RedirectView rv = new RedirectView(); + rv.setApplicationContext(wac); // Init RedirectView with WebAppCxt + rv.setUrl("/path"); + + HttpServletRequest request = new MockHttpServletRequest(); + request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); + HttpServletResponse response = new MockHttpServletResponse(); + + EasyMock.expect(mockProcessor.processUrl(request, "/path")).andReturn("/path?key=123"); + EasyMock.replay(mockProcessor); + + rv.render(new ModelMap(), request, response); + + EasyMock.verify(mockProcessor); + } + + + @Test + public void updateTargetUrlWithContextLoader() throws Exception { + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.registerSingleton("requestDataValueProcessor", RequestDataValueProcessorWrapper.class); + + MockServletContext servletContext = new MockServletContext(); + ContextLoader contextLoader = new ContextLoader(wac); + contextLoader.initWebApplicationContext(servletContext); + + try { + RequestDataValueProcessor mockProcessor = createMock(RequestDataValueProcessor.class); + wac.getBean(RequestDataValueProcessorWrapper.class).setRequestDataValueProcessor(mockProcessor); + + RedirectView rv = new RedirectView(); + rv.setUrl("/path"); + + HttpServletRequest request = new MockHttpServletRequest(); + HttpServletResponse response = new MockHttpServletResponse(); + + EasyMock.expect(mockProcessor.processUrl(request, "/path")).andReturn("/path?key=123"); + EasyMock.replay(mockProcessor); + + rv.render(new ModelMap(), request, response); + + EasyMock.verify(mockProcessor); + } + finally { + contextLoader.closeWebApplicationContext(servletContext); + } + } @Test public void emptyMap() throws Exception { @@ -296,7 +361,7 @@ public class RedirectViewTests { expectedUrlForEncoding = "/context" + expectedUrlForEncoding; expect(request.getContextPath()).andReturn("/context"); } - + HttpServletResponse response = createMock("response", HttpServletResponse.class); expect(response.encodeRedirectURL(expectedUrlForEncoding)).andReturn(expectedUrlForEncoding); response.sendRedirect(expectedUrlForEncoding); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java index b14ef64526..df855ee2ec 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java @@ -158,6 +158,7 @@ public class ViewResolverTests { view = vr.resolveViewName("redirect:myUrl", Locale.getDefault()); assertEquals("Correct view class", RedirectView.class, view.getClass()); assertEquals("Correct URL", "myUrl", ((RedirectView) view).getUrl()); + assertSame("View not initialized as bean", wac, ((RedirectView) view).getApplicationContext()); view = vr.resolveViewName("forward:myUrl", Locale.getDefault()); assertEquals("Correct view class", InternalResourceView.class, view.getClass());