From 92f8446eea53a2c05e75e5b3921fc155fc3eb4b0 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 10 Jan 2012 10:47:47 -0500 Subject: [PATCH 01/20] SPR-8997 Add HttpServletResponse to FlashMapManager contract. This change makes the HttpServletResponse available to the methods of FlashMapManager in addition to the HttpServletRequest. --- .../web/servlet/DispatcherServlet.java | 4 +- .../springframework/web/servlet/FlashMap.java | 2 + .../web/servlet/FlashMapManager.java | 7 ++- .../support/DefaultFlashMapManager.java | 28 +++++++--- .../support/DefaultFlashMapManagerTests.java | 54 ++++++++++--------- 5 files changed, 58 insertions(+), 37 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 1875eead59e..da5bb9b7121 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -815,7 +815,7 @@ public class DispatcherServlet extends FrameworkServlet { } } - this.flashMapManager.requestStarted(request); + this.flashMapManager.requestStarted(request, response); // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); @@ -827,7 +827,7 @@ public class DispatcherServlet extends FrameworkServlet { doDispatch(request, response); } finally { - this.flashMapManager.requestCompleted(request); + this.flashMapManager.requestCompleted(request, response); // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java index 0b7bc82ad03..cb6450ca89b 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java @@ -63,6 +63,8 @@ public final class FlashMap extends HashMap implements Comparabl /** * Create a new instance with an id uniquely identifying the creator of * this FlashMap. + * @param createdBy identifies the FlashMapManager instance that created + * and will manage this FlashMap instance (e.g. via a hashCode) */ public FlashMap(int createdBy) { this.createdBy = createdBy; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMapManager.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMapManager.java index 066da4231d8..6d765ec8473 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMapManager.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMapManager.java @@ -17,6 +17,7 @@ package org.springframework.web.servlet; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * A strategy interface for storing, retrieving, and managing {@code FlashMap} @@ -64,8 +65,9 @@ public interface FlashMapManager { *
  • Clean expired FlashMap instances. * * @param request the current request + * @param response the current response */ - void requestStarted(HttpServletRequest request); + void requestStarted(HttpServletRequest request, HttpServletResponse response); /** * Start the expiration period of the "output" FlashMap save it in the @@ -73,7 +75,8 @@ public interface FlashMapManager { *

    The "output" FlashMap should not be saved if it is empty or if it was * not created by the current FlashMapManager instance. * @param request the current request + * @param response the current response */ - void requestCompleted(HttpServletRequest request); + void requestCompleted(HttpServletRequest request, HttpServletResponse response); } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java index c10bb36f04d..6d6a978a336 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; @@ -64,7 +65,7 @@ public class DefaultFlashMapManager implements FlashMapManager { * {@inheritDoc} *

    An HTTP session is never created by this method. */ - public void requestStarted(HttpServletRequest request) { + public final void requestStarted(HttpServletRequest request, HttpServletResponse response) { if (request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE) != null) { return; } @@ -164,9 +165,9 @@ public class DefaultFlashMapManager implements FlashMapManager { } /** - * Iterate all flash maps and remove expired ones. + * Check and remove expired FlashMaps instances. */ - private void removeExpiredFlashMaps(HttpServletRequest request) { + protected void removeExpiredFlashMaps(HttpServletRequest request) { List allMaps = retrieveFlashMaps(request, false); if (CollectionUtils.isEmpty(allMaps)) { return; @@ -189,7 +190,7 @@ public class DefaultFlashMapManager implements FlashMapManager { * {@inheritDoc} *

    An HTTP session is never created if the "output" FlashMap is empty. */ - public void requestCompleted(HttpServletRequest request) { + public void requestCompleted(HttpServletRequest request, HttpServletResponse response) { FlashMap flashMap = (FlashMap) request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE); if (flashMap == null) { throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created"); @@ -198,24 +199,35 @@ public class DefaultFlashMapManager implements FlashMapManager { if (logger.isDebugEnabled()) { logger.debug("Saving FlashMap=" + flashMap); } - onSaveFlashMap(flashMap, request); - retrieveFlashMaps(request, true).add(flashMap); + onSaveFlashMap(flashMap, request, response); + saveFlashMap(flashMap, request, response); } } /** - * Update a FlashMap before it is stored in the HTTP Session. + * Update a FlashMap before it is stored in the underlying storage. *

    The default implementation starts the expiration period and ensures the * target request path is decoded and normalized if it is relative. * @param flashMap the flash map to be saved * @param request the current request + * @param response the current response */ - protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request) { + protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) { String targetPath = flashMap.getTargetRequestPath(); flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request)); flashMap.startExpirationPeriod(this.flashTimeout); } + /** + * Save the FlashMap in the underlying storage. + * @param flashMap the FlashMap to save + * @param request the current request + * @param response the current response + */ + protected void saveFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) { + retrieveFlashMaps(request, true).add(flashMap); + } + private String decodeAndNormalizePath(String path, HttpServletRequest request) { if (path != null) { path = this.urlPathHelper.decodeRequestString(request, path); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/DefaultFlashMapManagerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/DefaultFlashMapManagerTests.java index 61aaa808e6c..eb7414633de 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/DefaultFlashMapManagerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/support/DefaultFlashMapManagerTests.java @@ -32,6 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.servlet.FlashMap; import org.springframework.web.util.WebUtils; @@ -46,15 +47,18 @@ public class DefaultFlashMapManagerTests { private MockHttpServletRequest request; + private MockHttpServletResponse response; + @Before public void setup() { this.flashMapManager = new DefaultFlashMapManager(); this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); } @Test public void requestStarted() { - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); assertNotNull("Current FlashMap not found", flashMap); @@ -64,7 +68,7 @@ public class DefaultFlashMapManagerTests { public void requestStartedAlready() { FlashMap flashMap = new FlashMap(); this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertSame(flashMap, RequestContextUtils.getOutputFlashMap(request)); } @@ -79,7 +83,7 @@ public class DefaultFlashMapManagerTests { allMaps.add(flashMap); this.request.setRequestURI("/path"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); @@ -98,7 +102,7 @@ public class DefaultFlashMapManagerTests { this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts"); this.request.setRequestURI("/mvc/accounts"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); @@ -114,7 +118,7 @@ public class DefaultFlashMapManagerTests { allMaps.add(flashMap); this.request.setRequestURI("/path/"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); @@ -130,21 +134,21 @@ public class DefaultFlashMapManagerTests { allMaps.add(flashMap); this.request.setParameter("number", (String) null); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); clearFlashMapRequestAttributes(); this.request.setParameter("number", "two"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); clearFlashMapRequestAttributes(); this.request.setParameter("number", "one"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); @@ -163,14 +167,14 @@ public class DefaultFlashMapManagerTests { allMaps.add(flashMap); this.request.setParameter("id", "1"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); clearFlashMapRequestAttributes(); this.request.addParameter("id", "2"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); @@ -196,7 +200,7 @@ public class DefaultFlashMapManagerTests { Collections.shuffle(allMaps); this.request.setRequestURI("/one/two"); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(flashMapTwo, request.getAttribute(INPUT_FLASH_MAP_ATTRIBUTE)); } @@ -210,15 +214,15 @@ public class DefaultFlashMapManagerTests { flashMap.startExpirationPeriod(0); } Thread.sleep(100); - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); assertEquals(0, allMaps.size()); } @Test public void saveFlashMapWithoutAttributes() throws InterruptedException { - this.flashMapManager.requestStarted(this.request); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); + this.flashMapManager.requestCompleted(this.request, this.response); assertNull(getFlashMaps()); } @@ -227,19 +231,19 @@ public class DefaultFlashMapManagerTests { public void saveFlashMapNotCreatedByThisManager() throws InterruptedException { FlashMap flashMap = new FlashMap(); this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertNull(getFlashMaps()); } @Test public void saveFlashMapWithAttributes() throws InterruptedException { - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); flashMap.put("name", "value"); this.flashMapManager.setFlashMapTimeout(0); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); Thread.sleep(100); @@ -252,49 +256,49 @@ public class DefaultFlashMapManagerTests { @Test public void decodeTargetPath() throws InterruptedException { - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); flashMap.put("key", "value"); flashMap.setTargetRequestPath("/once%20upon%20a%20time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once upon a time", flashMap.getTargetRequestPath()); } @Test public void normalizeTargetPath() throws InterruptedException { - this.flashMapManager.requestStarted(this.request); + this.flashMapManager.requestStarted(this.request, this.response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); flashMap.put("key", "value"); flashMap.setTargetRequestPath("."); this.request.setRequestURI("/once/upon/a/time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once/upon/a", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("./"); this.request.setRequestURI("/once/upon/a/time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once/upon/a/", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath(".."); this.request.setRequestURI("/once/upon/a/time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once/upon", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("../"); this.request.setRequestURI("/once/upon/a/time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once/upon/", flashMap.getTargetRequestPath()); flashMap.setTargetRequestPath("../../only"); this.request.setRequestURI("/once/upon/a/time"); - this.flashMapManager.requestCompleted(this.request); + this.flashMapManager.requestCompleted(this.request, this.response); assertEquals("/once/only", flashMap.getTargetRequestPath()); } From d7d1b495f2775df0c03d7cff33d0a07c0e3e83dd Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 10 Jan 2012 16:02:00 +0100 Subject: [PATCH 02/20] Polish "Support flash attrs..." and related classes - Eliminate trailing whitespace - Update long method signatures to follow framework whitespace conventions Based on the following search, $ git grep -A3 '^.public .* .*([^\{;]*$' */src/main the strong convention throughout the framework when dealing with methods having long signatures (i.e. many parameters) is to break immediately after the opening paren, indent two tabs deeper and break lines around 90 characters as necessary. Such signatures should also be followed by a newline after the opening curly brace to break things up visually. The files edited in this commit had a particularly different style of intenting arguments to align with each other vertically, but the alignment only worked if one's tabstop is set at four spaces. When viewed at a different tabstop value, the effect is is jarring, both in that it is misaligned and significantly different from most of the framework. The convention described above reads well at any tabstop value. --- .../annotation/HttpEntityMethodProcessor.java | 31 ++++++----- .../ModelAndViewMethodReturnValueHandler.java | 29 ++++++----- ...dViewResolverMethodReturnValueHandler.java | 34 ++++++------ ...irectAttributesMethodArgumentResolver.java | 15 +++--- .../ServletInvocableHandlerMethod.java | 12 ++--- .../ServletRequestMethodArgumentResolver.java | 20 +++---- ...ServletResponseMethodArgumentResolver.java | 18 +++---- ...mponentsBuilderMethodArgumentResolver.java | 14 ++--- .../ViewMethodReturnValueHandler.java | 23 ++++---- .../ViewNameMethodReturnValueHandler.java | 23 ++++---- ...lAndViewMethodReturnValueHandlerTests.java | 30 +++++------ ...tractNamedValueMethodArgumentResolver.java | 35 +++++++------ .../AbstractWebArgumentResolverAdapter.java | 37 ++++++------- .../ErrorsMethodArgumentResolver.java | 19 +++---- .../method/annotation/MapMethodProcessor.java | 32 ++++++------ .../ModelAttributeMethodProcessor.java | 52 ++++++++++--------- .../annotation/ModelMethodProcessor.java | 34 ++++++------ ...equestHeaderMapMethodArgumentResolver.java | 23 ++++---- ...RequestParamMapMethodArgumentResolver.java | 23 ++++---- .../SessionStatusMethodArgumentResolver.java | 13 ++--- ...andlerMethodArgumentResolverComposite.java | 25 ++++----- ...dlerMethodReturnValueHandlerComposite.java | 29 ++++++----- .../support/InvocableHandlerMethod.java | 49 ++++++++--------- 23 files changed, 319 insertions(+), 301 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java index cbf434d4c04..04601a4d473 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,13 +39,13 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** * Resolves {@link HttpEntity} method argument values and also handles - * both {@link HttpEntity} and {@link ResponseEntity} return values. - * - *

    An {@link HttpEntity} return type has a set purpose. Therefore this - * handler should be configured ahead of handlers that support any return + * both {@link HttpEntity} and {@link ResponseEntity} return values. + * + *

    An {@link HttpEntity} return type has a set purpose. Therefore this + * handler should be configured ahead of handlers that support any return * value type annotated with {@code @ModelAttribute} or {@code @ResponseBody} * to ensure they don't take over. - * + * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1 @@ -66,10 +66,9 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws IOException, HttpMediaTypeNotSupportedException { HttpInputMessage inputMessage = createInputMessage(webRequest); @@ -100,11 +99,11 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro + "in method " + parameter.getMethod() + "is not parameterized"); } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { - + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + mavContainer.setRequestHandled(true); if (returnValue == null) { @@ -135,4 +134,4 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro } } -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java index b5494f50614..cca424048e4 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandler.java @@ -25,15 +25,15 @@ import org.springframework.web.servlet.SmartView; import org.springframework.web.servlet.View; /** - * Handles return values of type {@link ModelAndView} copying view and model + * Handles return values of type {@link ModelAndView} copying view and model * information to the {@link ModelAndViewContainer}. - * - *

    If the return value is {@code null}, the - * {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to + * + *

    If the return value is {@code null}, the + * {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to * {@code false} to indicate the request was handled directly. - * - *

    A {@link ModelAndView} return type has a set purpose. Therefore this - * handler should be configured ahead of handlers that support any return + * + *

    A {@link ModelAndView} return type has a set purpose. Therefore this + * handler should be configured ahead of handlers that support any return * value type annotated with {@code @ModelAttribute} or {@code @ResponseBody} * to ensure they don't take over. * @@ -41,20 +41,21 @@ import org.springframework.web.servlet.View; * @since 3.1 */ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler { - + public boolean supportsReturnType(MethodParameter returnType) { return ModelAndView.class.isAssignableFrom(returnType.getParameterType()); } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue == null) { mavContainer.setRequestHandled(true); return; } - + ModelAndView mav = (ModelAndView) returnValue; if (mav.isReference()) { String viewName = mav.getViewName(); @@ -75,4 +76,4 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn mavContainer.addAllAttributes(mav.getModel()); } -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandler.java index 121ad3ecc0c..d1ede55c576 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -29,25 +29,25 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; /** - * This return value handler is intended to be ordered after all others as it + * This return value handler is intended to be ordered after all others as it * attempts to handle _any_ return value type (i.e. returns {@code true} for * all return types). - * + * *

    The return value is handled either with a {@link ModelAndViewResolver} - * or otherwise by regarding it as a model attribute if it is a non-simple - * type. If neither of these succeeds (essentially simple type other than + * or otherwise by regarding it as a model attribute if it is a non-simple + * type. If neither of these succeeds (essentially simple type other than * String), {@link UnsupportedOperationException} is raised. - * - *

    Note: This class is primarily needed to support + * + *

    Note: This class is primarily needed to support * {@link ModelAndViewResolver}, which unfortunately cannot be properly - * adapted to the {@link HandlerMethodReturnValueHandler} contract since the + * adapted to the {@link HandlerMethodReturnValueHandler} contract since the * {@link HandlerMethodReturnValueHandler#supportsReturnType} method * cannot be implemented. Hence {@code ModelAndViewResolver}s are limited - * to always being invoked at the end after all other return value - * handlers have been given a chance. It is recommended to re-implement + * to always being invoked at the end after all other return value + * handlers have been given a chance. It is recommended to re-implement * a {@code ModelAndViewResolver} as {@code HandlerMethodReturnValueHandler}, * which also provides better access to the return type and method information. - * + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -71,10 +71,10 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth return true; } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest request) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest request) + throws Exception { if (this.mavResolvers != null) { for (ModelAndViewResolver mavResolver : this.mavResolvers) { @@ -93,7 +93,7 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth } } - // No suitable ModelAndViewResolver.. + // No suitable ModelAndViewResolver.. if (this.modelAttributeProcessor.supportsReturnType(returnType)) { this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request); @@ -104,4 +104,4 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth } } -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java index 272ab04ff9e..89d4849c3d4 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,8 +30,8 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap; /** - * Resolves method arguments of type {@link RedirectAttributes}. - * + * Resolves method arguments of type {@link RedirectAttributes}. + * *

    This resolver must be listed ahead of {@link org.springframework.web.method.annotation.ModelMethodProcessor} and * {@link org.springframework.web.method.annotation.MapMethodProcessor}, which support {@link Map} and {@link Model} * arguments both of which are "super" types of {@code RedirectAttributes} @@ -46,10 +46,11 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder); mavContainer.setRedirectModel(redirectAttributes); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 348be0654ac..3eb3261fb7d 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -89,9 +89,9 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { * @param mavContainer the {@link ModelAndViewContainer} for the current request * @param providedArgs argument values to try to use without the need for view resolution */ - public final void invokeAndHandle(NativeWebRequest request, - ModelAndViewContainer mavContainer, - Object...providedArgs) throws Exception { + public final void invokeAndHandle( + NativeWebRequest request, ModelAndViewContainer mavContainer, + Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(request, mavContainer, providedArgs); @@ -124,7 +124,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { sb.append("[value=" + returnValue + "]"); return getDetailedErrorMessage(sb.toString()); } - + /** * Set the response status according to the {@link ResponseStatus} annotation. */ @@ -157,4 +157,4 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { private boolean hasResponseStatus() { return responseStatus != null; } -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java index 6976861adbf..a6e9f7f9333 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -37,7 +37,7 @@ import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.servlet.support.RequestContextUtils; /** - * Resolves request-related method argument values of the following types: + * Resolves request-related method argument values of the following types: *

      *
    • {@link WebRequest} *
    • {@link ServletRequest} @@ -57,20 +57,20 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume public boolean supportsParameter(MethodParameter parameter) { Class paramType = parameter.getParameterType(); - return WebRequest.class.isAssignableFrom(paramType) || + return WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || - HttpSession.class.isAssignableFrom(paramType) || + HttpSession.class.isAssignableFrom(paramType) || Principal.class.isAssignableFrom(paramType) || - Locale.class.equals(paramType) || + Locale.class.equals(paramType) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws IOException { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws IOException { Class paramType = parameter.getParameterType(); if (WebRequest.class.isAssignableFrom(paramType)) { @@ -108,4 +108,4 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume } } -} \ No newline at end of file +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java index f0514bd4887..e9344b0027d 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletResponseMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -47,20 +47,20 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum public boolean supportsParameter(MethodParameter parameter) { Class paramType = parameter.getParameterType(); return ServletResponse.class.isAssignableFrom(paramType) - || OutputStream.class.isAssignableFrom(paramType) + || OutputStream.class.isAssignableFrom(paramType) || Writer.class.isAssignableFrom(paramType); } /** - * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to - * {@code false} to indicate that the method signature provides access - * to the response. If subsequently the underlying method returns + * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to + * {@code false} to indicate that the method signature provides access + * to the response. If subsequently the underlying method returns * {@code null}, the request is considered directly handled. */ - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws IOException { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws IOException { mavContainer.setRequestHandled(true); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/UriComponentsBuilderMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/UriComponentsBuilderMethodArgumentResolver.java index 2cf1905d1cb..f87811a950b 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/UriComponentsBuilderMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/UriComponentsBuilderMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,7 +28,7 @@ import org.springframework.web.util.UriComponentsBuilder; /** * Resolvers argument values of type {@link UriComponentsBuilder}. - * + * *

      The returned instance is initialized via * {@link ServletUriComponentsBuilder#fromServletMapping(HttpServletRequest)}. * @@ -41,11 +41,11 @@ public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethod return UriComponentsBuilder.class.isAssignableFrom(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { - + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); return ServletUriComponentsBuilder.fromServletMapping(request); } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewMethodReturnValueHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewMethodReturnValueHandler.java index 88a1b4d28e1..de6a387330c 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewMethodReturnValueHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -27,14 +27,14 @@ import org.springframework.web.servlet.View; /** * Handles return values that are of type {@link View}. * - *

      A {@code null} return value is left as-is leaving it to the configured - * {@link RequestToViewNameTranslator} to select a view name by convention. + *

      A {@code null} return value is left as-is leaving it to the configured + * {@link RequestToViewNameTranslator} to select a view name by convention. * - *

      A {@link View} return type has a set purpose. Therefore this handler - * should be configured ahead of handlers that support any return value type + *

      A {@link View} return type has a set purpose. Therefore this handler + * should be configured ahead of handlers that support any return value type * annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure * they don't take over. - * + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -44,10 +44,11 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan return View.class.isAssignableFrom(returnType.getParameterType()); } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue == null) { return; } @@ -62,7 +63,7 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan } else { // should not happen - throw new UnsupportedOperationException("Unexpected return type: " + + throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewNameMethodReturnValueHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewNameMethodReturnValueHandler.java index aa7dc816f00..153486abb5a 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewNameMethodReturnValueHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ViewNameMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,12 +26,12 @@ import org.springframework.web.servlet.RequestToViewNameTranslator; * Handles return values of types {@code void} and {@code String} interpreting * them as view name reference. * - *

      A {@code null} return value, either due to a {@code void} return type or - * as the actual return value is left as-is allowing the configured + *

      A {@code null} return value, either due to a {@code void} return type or + * as the actual return value is left as-is allowing the configured * {@link RequestToViewNameTranslator} to select a view name by convention. * - *

      A String return value can be interpreted in more than one ways depending - * on the presence of annotations like {@code @ModelAttribute} or + *

      A String return value can be interpreted in more than one ways depending + * on the presence of annotations like {@code @ModelAttribute} or * {@code @ResponseBody}. Therefore this handler should be configured after * the handlers that support these annotations. * @@ -45,10 +45,11 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu return (void.class.equals(paramType) || String.class.equals(paramType)); } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue == null) { return; } @@ -61,7 +62,7 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu } else { // should not happen - throw new UnsupportedOperationException("Unexpected return type: " + + throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } @@ -69,7 +70,7 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu /** * Whether the given view name is a redirect view reference. * @param viewName the view name to check, never {@code null} - * @return "true" if the given view name is recognized as a redirect view + * @return "true" if the given view name is recognized as a redirect view * reference; "false" otherwise. */ protected boolean isRedirectViewName(String viewName) { diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java index cfec1453542..ee9ccea2520 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java @@ -37,7 +37,7 @@ import org.springframework.web.servlet.view.RedirectView; /** * Test fixture with {@link ModelAndViewMethodReturnValueHandler}. - * + * * @author Rossen Stoyanchev */ public class ModelAndViewMethodReturnValueHandlerTests { @@ -57,7 +57,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { this.webRequest = new ServletWebRequest(new MockHttpServletRequest()); this.returnParamModelAndView = getReturnValueParam("modelAndView"); } - + @Test public void supportsReturnType() throws Exception { assertTrue(handler.supportsReturnType(returnParamModelAndView)); @@ -68,7 +68,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { public void handleViewReference() throws Exception { ModelAndView mav = new ModelAndView("viewName", "attrName", "attrValue"); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); - + assertEquals("viewName", mavContainer.getView()); assertEquals("attrValue", mavContainer.getModel().get("attrName")); } @@ -77,7 +77,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { public void handleViewInstance() throws Exception { ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue"); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); - + assertEquals(RedirectView.class, mavContainer.getView().getClass()); assertEquals("attrValue", mavContainer.getModel().get("attrName")); } @@ -85,7 +85,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { @Test public void handleNull() throws Exception { handler.handleReturnValue(null, returnParamModelAndView, mavContainer, webRequest); - + assertTrue(mavContainer.isRequestHandled()); } @@ -93,10 +93,10 @@ public class ModelAndViewMethodReturnValueHandlerTests { public void handleRedirectAttributesWithViewReference() throws Exception { RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); mavContainer.setRedirectModel(redirectAttributes); - + ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue"); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); - + assertEquals(RedirectView.class, mavContainer.getView().getClass()); assertEquals("attrValue", mavContainer.getModel().get("attrName")); assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes, @@ -107,24 +107,24 @@ public class ModelAndViewMethodReturnValueHandlerTests { public void handleRedirectAttributesWithViewInstance() throws Exception { RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); mavContainer.setRedirectModel(redirectAttributes); - + ModelAndView mav = new ModelAndView("redirect:viewName", "attrName", "attrValue"); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); - + ModelMap model = mavContainer.getModel(); assertEquals("redirect:viewName", mavContainer.getViewName()); assertEquals("attrValue", model.get("attrName")); assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes, model); } - + @Test public void handleRedirectAttributesWithoutRedirect() throws Exception { RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); mavContainer.setRedirectModel(redirectAttributes); - + ModelAndView mav = new ModelAndView(); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); - + ModelMap model = mavContainer.getModel(); assertEquals(null, mavContainer.getView()); assertTrue(mavContainer.getModel().isEmpty()); @@ -136,7 +136,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { Method method = getClass().getDeclaredMethod(methodName); return new MethodParameter(method, -1); } - + ModelAndView modelAndView() { return null; } @@ -144,5 +144,5 @@ public class ModelAndViewMethodReturnValueHandlerTests { String viewName() { return null; } - -} \ No newline at end of file + +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java index dceda243b5e..b92133765ef 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -35,8 +35,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and - * path variables are examples of named values. Each may have a name, a required flag, and a default value. + * Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and + * path variables are examples of named values. Each may have a name, a required flag, and a default value. *

      Subclasses define how to do the following: *

        *
      • Obtain named value information for a method parameter @@ -44,11 +44,11 @@ import org.springframework.web.method.support.ModelAndViewContainer; *
      • Handle missing argument values when argument values are required *
      • Optionally handle a resolved value *
      - *

      A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions. + *

      A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions. * For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor. - *

      A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't + *

      A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't * match the method parameter type. - * + * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1 @@ -63,7 +63,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle new ConcurrentHashMap(); /** - * @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions + * @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions * in default values, or {@code null} if default values are not expected to contain expressions */ public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { @@ -71,10 +71,11 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null; } - public final Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public final Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + Class paramType = parameter.getParameterType(); NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); @@ -95,7 +96,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); arg = binder.convertIfNecessary(arg, paramType, parameter); } - + handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; @@ -115,9 +116,9 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle } /** - * Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically + * Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically * retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}. - * + * * @param parameter the method parameter * @return the named value information */ @@ -136,7 +137,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue); return new NamedValueInfo(name, info.required, defaultValue); } - + /** * Resolves the given parameter type and value name into an argument value. * @param name the name of the value being resolved @@ -165,7 +166,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle } /** - * Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)} + * Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)} * returned {@code null} and there is no default value. Subclasses typically throw an exception in this case. * @param name the name for the value * @param parameter the method parameter @@ -219,4 +220,4 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle this.defaultValue = defaultValue; } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractWebArgumentResolverAdapter.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractWebArgumentResolverAdapter.java index af7e95c2aca..76e16303183 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractWebArgumentResolverAdapter.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/AbstractWebArgumentResolverAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,16 +28,16 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** - * An abstract base class adapting a {@link WebArgumentResolver} to the - * {@link HandlerMethodArgumentResolver} contract. - * + * An abstract base class adapting a {@link WebArgumentResolver} to the + * {@link HandlerMethodArgumentResolver} contract. + * *

      Note: This class is provided for backwards compatibility. - * However it is recommended to re-write a {@code WebArgumentResolver} as - * {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter} - * can only be implemented by actually resolving the value and then checking - * the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions - * raised must be absorbed and ignored since it's not clear whether the adapter - * doesn't support the parameter or whether it failed for an internal reason. + * However it is recommended to re-write a {@code WebArgumentResolver} as + * {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter} + * can only be implemented by actually resolving the value and then checking + * the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions + * raised must be absorbed and ignored since it's not clear whether the adapter + * doesn't support the parameter or whether it failed for an internal reason. * The {@code HandlerMethodArgumentResolver} contract also provides access to * model attributes and to {@code WebDataBinderFactory} (for type conversion). * @@ -60,7 +60,7 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho } /** - * Actually resolve the value and check the resolved value is not + * Actually resolve the value and check the resolved value is not * {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions. */ public boolean supportsParameter(MethodParameter parameter) { @@ -88,21 +88,22 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho /** * Delegate to the {@link WebArgumentResolver} instance. - * @exception IllegalStateException if the resolved value is not assignable + * @exception IllegalStateException if the resolved value is not assignable * to the method parameter. */ - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + Class paramType = parameter.getParameterType(); Object result = this.adaptee.resolveArgument(parameter, webRequest); if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) { throw new IllegalStateException( - "Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() + + "Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() + "resolved to incompatible value of type [" + (result != null ? result.getClass() : null) + "]. Consider declaring the argument type in a less specific fashion."); } return result; } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java index 89d7af07058..2caca5b17c6 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -29,12 +29,12 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** * Resolves {@link Errors} method arguments. - * + * *

      An {@code Errors} method argument is expected to appear immediately after * the model attribute in the method signature. It is resolved by expecting the * last two attributes added to the model to be the model attribute and its - * {@link BindingResult}. - * + * {@link BindingResult}. + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -45,10 +45,11 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv return Errors.class.isAssignableFrom(paramType); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + ModelMap model = mavContainer.getModel(); if (model.size() > 0) { int lastIndex = model.size()-1; @@ -63,4 +64,4 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv "argument in the controller method signature: " + parameter.getMethod()); } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java index 064a76e85a5..df430b5f0d1 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -27,12 +27,12 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** * Resolves {@link Map} method arguments and handles {@link Map} return values. - * - *

      A Map return value can be interpreted in more than one ways depending - * on the presence of annotations like {@code @ModelAttribute} or + * + *

      A Map return value can be interpreted in more than one ways depending + * on the presence of annotations like {@code @ModelAttribute} or * {@code @ResponseBody}. Therefore this handler should be configured after * the handlers that support these annotations. - * + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -42,10 +42,11 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle return Map.class.isAssignableFrom(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + return mavContainer.getModel(); } @@ -54,10 +55,11 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle } @SuppressWarnings({ "unchecked", "rawtypes" }) - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue == null) { return; } @@ -66,8 +68,8 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle } else { // should not happen - throw new UnsupportedOperationException("Unexpected return type: " + + throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index a1521296aa6..5ac23425f61 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,15 +39,15 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** * Resolves method arguments annotated with {@code @ModelAttribute} and handles * return values from methods annotated with {@code @ModelAttribute}. - * - *

      Model attributes are obtained from the model or if not found possibly - * created with a default constructor if it is available. Once created, the - * attributed is populated with request data via data binding and also - * validation may be applied if the argument is annotated with + * + *

      Model attributes are obtained from the model or if not found possibly + * created with a default constructor if it is available. Once created, the + * attributed is populated with request data via data binding and also + * validation may be applied if the argument is annotated with * {@code @javax.validation.Valid}. * - *

      When this handler is created with {@code annotationNotRequired=true}, - * any non-simple type argument and return value is regarded as a model + *

      When this handler is created with {@code annotationNotRequired=true}, + * any non-simple type argument and return value is regarded as a model * attribute with or without the presence of an {@code @ModelAttribute}. * * @author Rossen Stoyanchev @@ -58,10 +58,10 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol protected Log logger = LogFactory.getLog(this.getClass()); private final boolean annotationNotRequired; - + /** * @param annotationNotRequired if "true", non-simple method arguments and - * return values are considered model attributes with or without a + * return values are considered model attributes with or without a * {@code @ModelAttribute} annotation. */ public ModelAttributeMethodProcessor(boolean annotationNotRequired) { @@ -85,18 +85,19 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol } /** - * Resolve the argument from the model or if not found instantiate it with - * its default if it is available. The model attribute is then populated + * Resolve the argument from the model or if not found instantiate it with + * its default if it is available. The model attribute is then populated * with request values via data binding and optionally validated * if {@code @java.validation.Valid} is present on the argument. * @throws BindException if data binding and validation result in an error * and the next method parameter is not of type {@link Errors}. * @throws Exception if WebDataBinder initialization fails. */ - public final Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest request, - WebDataBinderFactory binderFactory) throws Exception { + public final Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest request, WebDataBinderFactory binderFactory) + throws Exception { + String name = ModelFactory.getNameForParameter(parameter); Object target = (mavContainer.containsAttribute(name)) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request); @@ -130,7 +131,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol return BeanUtils.instantiateClass(parameter.getParameterType()); } - + /** * Extension point to bind the request to the target object. * @param binder the data binder instance to use for the binding @@ -158,7 +159,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol /** * Whether to raise a {@link BindException} on bind or validation errors. - * The default implementation returns {@code true} if the next method + * The default implementation returns {@code true} if the next method * argument is not of type {@link Errors}. * @param binder the data binder used to perform data binding * @param parameter the method argument @@ -167,12 +168,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol int i = parameter.getParameterIndex(); Class[] paramTypes = parameter.getMethod().getParameterTypes(); boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1])); - + return !hasBindingResult; } /** - * Return {@code true} if there is a method-level {@code @ModelAttribute} + * Return {@code true} if there is a method-level {@code @ModelAttribute} * or if it is a non-simple type when {@code annotationNotRequired=true}. */ public boolean supportsReturnType(MethodParameter returnType) { @@ -190,13 +191,14 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol /** * Add non-null return values to the {@link ModelAndViewContainer}. */ - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue != null) { String name = ModelFactory.getNameForReturnValue(returnValue, returnType); mavContainer.addAttribute(name, returnValue); } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelMethodProcessor.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelMethodProcessor.java index b3952424e2b..c529efd8a3d 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelMethodProcessor.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/ModelMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -25,13 +25,13 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves {@link Model} arguments and handles {@link Model} return values. - * - *

      A {@link Model} return type has a set purpose. Therefore this handler - * should be configured ahead of handlers that support any return value type + * Resolves {@link Model} arguments and handles {@link Model} return values. + * + *

      A {@link Model} return type has a set purpose. Therefore this handler + * should be configured ahead of handlers that support any return value type * annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure * they don't take over. - * + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -41,10 +41,11 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand return Model.class.isAssignableFrom(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + return mavContainer.getModel(); } @@ -52,10 +53,11 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand return Model.class.isAssignableFrom(returnType.getParameterType()); } - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + if (returnValue == null) { return; } @@ -64,8 +66,8 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand } else { // should not happen - throw new UnsupportedOperationException("Unexpected return type: " + + throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java index a58d96cc270..4843782efb9 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -31,13 +31,13 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves {@link Map} method arguments annotated with {@code @RequestHeader}. - * For individual header values annotated with {@code @RequestHeader} see + * Resolves {@link Map} method arguments annotated with {@code @RequestHeader}. + * For individual header values annotated with {@code @RequestHeader} see * {@link RequestHeaderMethodArgumentResolver} instead. - * - *

      The created {@link Map} contains all request header name/value pairs. + * + *

      The created {@link Map} contains all request header name/value pairs. * The method parameter type may be a {@link MultiValueMap} to receive all - * values for a header, not only the first one. + * values for a header, not only the first one. * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -50,10 +50,11 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu && Map.class.isAssignableFrom(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + Class paramType = parameter.getParameterType(); if (MultiValueMap.class.isAssignableFrom(paramType)) { @@ -82,4 +83,4 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu return result; } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java index 435aafdbb8e..befe7178d9d 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,12 +30,12 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not - * specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map} + * Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not + * specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map} * method arguments with a request parameter name. - * - *

      The created {@link Map} contains all request parameter name/value pairs. If the method parameter type - * is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for + * + *

      The created {@link Map} contains all request parameter name/value pairs. If the method parameter type + * is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for * cases where request parameters have multiple values. * * @author Arjen Poutsma @@ -55,10 +55,11 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum return false; } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + Class paramType = parameter.getParameterType(); Map parameterMap = webRequest.getParameterMap(); @@ -81,4 +82,4 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum return result; } } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/SessionStatusMethodArgumentResolver.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/SessionStatusMethodArgumentResolver.java index 3f20380045b..f19ae737275 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/SessionStatusMethodArgumentResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/SessionStatusMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,7 +24,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves a {@link SessionStatus} argument by obtaining it from + * Resolves a {@link SessionStatus} argument by obtaining it from * the {@link ModelAndViewContainer}. * * @author Rossen Stoyanchev @@ -36,10 +36,11 @@ public class SessionStatusMethodArgumentResolver implements HandlerMethodArgumen return SessionStatus.class.equals(parameter.getParameterType()); } - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + return mavContainer.getSessionStatus(); } diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java index c6c6f8ee33c..b01482d953f 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -31,8 +31,8 @@ import org.springframework.web.context.request.NativeWebRequest; /** * Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s. - * Previously resolved method parameters are cached for faster lookups. - * + * Previously resolved method parameters are cached for faster lookups. + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -40,12 +40,12 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu protected final Log logger = LogFactory.getLog(getClass()); - private final List argumentResolvers = + private final List argumentResolvers = new ArrayList(); private final Map argumentResolverCache = new ConcurrentHashMap(); - + /** * Return a read-only list with the contained resolvers, or an empty list. */ @@ -54,7 +54,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu } /** - * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered + * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered * {@link HandlerMethodArgumentResolver}. */ public boolean supportsParameter(MethodParameter parameter) { @@ -65,10 +65,11 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it. * @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found. */ - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { + public Object resolveArgument( + MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) + throws Exception { + HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]"); return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); @@ -94,7 +95,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu } return result; } - + /** * Add the given {@link HandlerMethodArgumentResolver}. */ @@ -116,4 +117,4 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu return this; } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java index 7652c879efa..f06d458cbd9 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -29,17 +29,17 @@ import org.springframework.util.Assert; import org.springframework.web.context.request.NativeWebRequest; /** - * Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s. - * Previously resolved return types are cached for faster lookups. - * + * Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s. + * Previously resolved return types are cached for faster lookups. + * * @author Rossen Stoyanchev * @since 3.1 */ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { - + protected final Log logger = LogFactory.getLog(getClass()); - private final List returnValueHandlers = + private final List returnValueHandlers = new ArrayList(); private final Map returnValueHandlerCache = @@ -53,7 +53,7 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe } /** - * Whether the given {@linkplain MethodParameter method return type} is supported by any registered + * Whether the given {@linkplain MethodParameter method return type} is supported by any registered * {@link HandlerMethodReturnValueHandler}. */ public boolean supportsReturnType(MethodParameter returnType) { @@ -64,10 +64,11 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it. * @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found. */ - public void handleReturnValue(Object returnValue, - MethodParameter returnType, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { + public void handleReturnValue( + Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) + throws Exception { + HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); @@ -92,8 +93,8 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe } } return result; - } - + } + /** * Add the given {@link HandlerMethodReturnValueHandler}. */ @@ -115,4 +116,4 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe return this; } -} \ No newline at end of file +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index f6620bea35c..1ce366f52a7 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -32,16 +32,16 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.HandlerMethod; /** - * Provides a method for invoking the handler method for a given request after resolving its method argument + * Provides a method for invoking the handler method for a given request after resolving its method argument * values through registered {@link HandlerMethodArgumentResolver}s. - * - *

      Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion. + * + *

      Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion. * Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply a binder factory to pass to - * argument resolvers. - * - *

      Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize + * argument resolvers. + * + *

      Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize * the list of argument resolvers. - * + * * @author Rossen Stoyanchev * @since 3.1 */ @@ -99,20 +99,20 @@ public class InvocableHandlerMethod extends HandlerMethod { } /** - * Invoke the method after resolving its argument values in the context of the given request.

      Argument - * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs} - * parameter however may supply argument values to be used directly, i.e. without argument resolution. - * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or + * Invoke the method after resolving its argument values in the context of the given request.

      Argument + * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs} + * parameter however may supply argument values to be used directly, i.e. without argument resolution. + * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or * a thrown exception instance. Provided argument values are checked before argument resolvers. - * + * * @param request the current request * @param mavContainer the {@link ModelAndViewContainer} for the current request * @param providedArgs argument values to try to use without view resolution * @return the raw value returned by the invoked method - * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception + * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception */ - public final Object invokeForRequest(NativeWebRequest request, - ModelAndViewContainer mavContainer, + public final Object invokeForRequest(NativeWebRequest request, + ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); @@ -135,9 +135,10 @@ public class InvocableHandlerMethod extends HandlerMethod { /** * Get the method argument values for the current request. */ - private Object[] getMethodArgumentValues(NativeWebRequest request, - ModelAndViewContainer mavContainer, - Object... providedArgs) throws Exception { + private Object[] getMethodArgumentValues( + NativeWebRequest request, ModelAndViewContainer mavContainer, + Object... providedArgs) throws Exception { + MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { @@ -187,7 +188,7 @@ public class InvocableHandlerMethod extends HandlerMethod { sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); return sb.toString(); } - + /** * Attempt to resolve a method parameter from the list of provided argument values. */ @@ -202,7 +203,7 @@ public class InvocableHandlerMethod extends HandlerMethod { } return null; } - + /** * Invoke the handler method with the given argument values. */ @@ -215,7 +216,7 @@ public class InvocableHandlerMethod extends HandlerMethod { String msg = getInvocationErrorMessage(e.getMessage(), args); throw new IllegalArgumentException(msg, e); } - catch (InvocationTargetException e) { + catch (InvocationTargetException e) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = e.getTargetException(); if (targetException instanceof RuntimeException) { @@ -233,7 +234,7 @@ public class InvocableHandlerMethod extends HandlerMethod { } } } - + private String getInvocationErrorMessage(String message, Object[] resolvedArgs) { StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message)); sb.append("Resolved arguments: \n"); @@ -250,4 +251,4 @@ public class InvocableHandlerMethod extends HandlerMethod { return sb.toString(); } -} \ No newline at end of file +} From 5a2bd97c3765cb991f7feb6d7578e8974f9e2e57 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 10 Jan 2012 22:24:14 -0500 Subject: [PATCH 03/20] SPR-8997 Add getter for access to flash timeout. --- .../support/DefaultFlashMapManager.java | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java index 6d6a978a336..f4b1a04a5f1 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; @@ -48,17 +49,39 @@ public class DefaultFlashMapManager implements FlashMapManager { private static final Log logger = LogFactory.getLog(DefaultFlashMapManager.class); - private int flashTimeout = 180; + private int flashMapTimeout = 180; - private final UrlPathHelper urlPathHelper = new UrlPathHelper(); + private UrlPathHelper urlPathHelper = new UrlPathHelper(); /** * Set the amount of time in seconds after a {@link FlashMap} is saved * (at request completion) and before it expires. *

      The default value is 180 seconds. */ - public void setFlashMapTimeout(int flashTimeout) { - this.flashTimeout = flashTimeout; + public void setFlashMapTimeout(int flashMapTimeout) { + this.flashMapTimeout = flashMapTimeout; + } + + /** + * Return the amount of time in seconds before a FlashMap expires. + */ + public int getFlashMapTimeout() { + return flashMapTimeout; + } + + /** + * Set the UrlPathHelper to use to obtain the request URI. + */ + public void setUrlPathHelper(UrlPathHelper urlPathHelper) { + Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); + this.urlPathHelper = urlPathHelper; + } + + /** + * Return the UrlPathHelper implementation for the request URI. + */ + public UrlPathHelper getUrlPathHelper() { + return urlPathHelper; } /** @@ -215,7 +238,7 @@ public class DefaultFlashMapManager implements FlashMapManager { protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) { String targetPath = flashMap.getTargetRequestPath(); flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request)); - flashMap.startExpirationPeriod(this.flashTimeout); + flashMap.startExpirationPeriod(this.flashMapTimeout); } /** From ab6a7e8e740582a6f747ef54d4f6d633cd96354e Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 11 Jan 2012 11:13:01 +0100 Subject: [PATCH 04/20] Polishing --- .../oxm/jaxb/Jaxb2Marshaller.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index 12cae278baa..d97b393a035 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -65,6 +65,14 @@ import javax.xml.transform.sax.SAXSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.ls.LSResourceResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.annotation.AnnotationUtils; @@ -87,14 +95,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.StaxUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.w3c.dom.ls.LSResourceResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - /** * Implementation of the Marshaller interface for JAXB 2.0. * @@ -161,7 +161,7 @@ public class Jaxb2Marshaller private boolean supportJaxbElementClass = false; - private LSResourceResolver schemaResourceResolver; + private LSResourceResolver schemaResourceResolver; /** @@ -289,17 +289,18 @@ public class Jaxb2Marshaller this.schemaLanguage = schemaLanguage; } - /** - * Sets the resource resolver, as used to load the schema resources. - * @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver) - * @see #setSchema(Resource) - * @see #setSchemas(Resource[]) - */ - public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) { - this.schemaResourceResolver = schemaResourceResolver; - } + /** + * Sets the resource resolver, as used to load the schema resources. + * + * @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver) + * @see #setSchema(Resource) + * @see #setSchemas(Resource[]) + */ + public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) { + this.schemaResourceResolver = schemaResourceResolver; + } - /** + /** * Specify whether MTOM support should be enabled or not. * Default is false: marshalling using XOP/MTOM not being enabled. */ @@ -420,9 +421,9 @@ public class Jaxb2Marshaller schemaSources[i] = new SAXSource(xmlReader, inputSource); } SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage); - if (schemaResourceResolver != null) { - schemaFactory.setResourceResolver(schemaResourceResolver); - } + if (schemaResourceResolver != null) { + schemaFactory.setResourceResolver(schemaResourceResolver); + } return schemaFactory.newSchema(schemaSources); } From 6da6acbe54709d0934074ef2964887bafa5112ac Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 11 Jan 2012 11:10:52 -0500 Subject: [PATCH 05/20] Make AbstractHandlerMethodExceptionResolver an abstract class. --- .../resources/changelog.txt | 2 +- ...bstractHandlerMethodExceptionResolver.java | 26 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index 2a723d7b2cf..231d287b01b 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -20,7 +20,7 @@ Changes in version 3.1.1 (2012-02-06) * Hibernate 4 LocalSessionFactoryBean does not insist on a "dataSource" reference being set * added "entityInterceptor" property to Hibernate 4 LocalSessionFactoryBean * corrected fix for QuartzJobBean to work with Quartz 2.0/2.1 - +* Make AbstractHandlerMethodExceptionResolver an abstract class Changes in version 3.1 GA (2011-12-12) -------------------------------------- diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java index edf5a770f19..4481118b3c2 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java @@ -23,13 +23,14 @@ import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; /** - * Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver} - * implementations that support handling exceptions from {@link HandlerMethod}s rather than handlers. + * Abstract base class for + * {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver} + * implementations that support handling exceptions from handlers of type {@link HandlerMethod}. * * @author Rossen Stoyanchev * @since 3.1 */ -public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver { +public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver { /** * Checks if the handler is a {@link HandlerMethod} instance and performs the check against the bean @@ -52,10 +53,10 @@ public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExcep } @Override - protected final ModelAndView doResolveException(HttpServletRequest request, - HttpServletResponse response, - Object handler, - Exception ex) { + protected final ModelAndView doResolveException( + HttpServletRequest request, HttpServletResponse response, + Object handler, Exception ex) { + return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); } @@ -73,11 +74,8 @@ public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExcep * @param ex the exception that got thrown during handler execution * @return a corresponding ModelAndView to forward to, or null for default processing */ - protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, - HttpServletResponse response, - HandlerMethod handlerMethod, - Exception ex) { - return null; - } - + protected abstract ModelAndView doResolveHandlerMethodException( + HttpServletRequest request, HttpServletResponse response, + HandlerMethod handlerMethod, Exception ex); + } From be4e6984836a087f52a160f0c92964a64937d814 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 12 Jan 2012 17:13:11 -0500 Subject: [PATCH 06/20] SPR-9021 Correct issue in comparing Accept header media types. When checking for an exact match of Accept header media types additional parameters such as quality need to be excluded. For example "*/*" matches "*/*;q=0.9". --- .../mvc/condition/ProducesRequestCondition.java | 6 ++++-- .../condition/ProducesRequestConditionTests.java | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java index 0e46ffe4e1e..78cee038273 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java @@ -173,7 +173,7 @@ public final class ProducesRequestCondition extends AbstractRequestCondition *

    • Sort 'Accept' header media types by quality value via * {@link MediaType#sortByQualityValue(List)} and iterate the list. - *
    • Get the lowest index of matching media types from each "produces" + *
    • Get the first index of matching media types in each "produces" * condition first matching with {@link MediaType#equals(Object)} and * then with {@link MediaType#includes(MediaType)}. *
    • If a lower index is found, the condition at that index wins. @@ -220,7 +220,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition 0); } + // SPR-9021 + + @Test + public void compareToMediaTypeAllWithParameter() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Accept", "*/*;q=0.9"); + + ProducesRequestCondition condition1 = new ProducesRequestCondition(); + ProducesRequestCondition condition2 = new ProducesRequestCondition("application/json"); + + assertTrue(condition1.compareTo(condition2, request) < 0); + assertTrue(condition2.compareTo(condition1, request) > 0); + } + @Test public void compareToEqualMatch() { MockHttpServletRequest request = new MockHttpServletRequest(); From b0c735feaedaca6b0dddca37193dd5ddd1e6c3e0 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 13 Jan 2012 13:18:53 -0500 Subject: [PATCH 07/20] SPR-8976 Make flash attrs available to view controllers. Previously flash attributes were automatically merged into the model of annotated controllers only. This change extends the same benefit to ParameterizableView- and UrlFilenameViewController, both of which merely select a view without user controller logic and (the views) would otherwise not have access to the flash attributes. --- .../mvc/AbstractUrlViewController.java | 6 +- .../mvc/ParameterizableViewController.java | 4 +- .../ParameterizableViewControllerTests.java | 71 +++++++++++++++++++ .../mvc/UrlFilenameViewControllerTests.java | 13 ++++ 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/ParameterizableViewControllerTests.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/AbstractUrlViewController.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/AbstractUrlViewController.java index 746a9b1bc40..2e7056342d0 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/AbstractUrlViewController.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/AbstractUrlViewController.java @@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.util.Assert; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.util.UrlPathHelper; /** @@ -86,7 +87,8 @@ public abstract class AbstractUrlViewController extends AbstractController { /** * Retrieves the URL path to use for lookup and delegates to - * {@link #getViewNameForRequest}. + * {@link #getViewNameForRequest}. Also adds the content of + * {@link RequestContextUtils#getInputFlashMap} to the model. */ @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { @@ -95,7 +97,7 @@ public abstract class AbstractUrlViewController extends AbstractController { if (logger.isDebugEnabled()) { logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]"); } - return new ModelAndView(viewName); + return new ModelAndView(viewName, RequestContextUtils.getInputFlashMap(request)); } /** diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/ParameterizableViewController.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/ParameterizableViewController.java index b444e003494..68499f29455 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/ParameterizableViewController.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/ParameterizableViewController.java @@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.support.RequestContextUtils; /** *

      Trivial controller that always returns a named view. The view @@ -87,12 +88,13 @@ public class ParameterizableViewController extends AbstractController { /** * Return a ModelAndView object with the specified view name. + * The content of {@link RequestContextUtils#getInputFlashMap} is also added to the model. * @see #getViewName() */ @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { - return new ModelAndView(getViewName()); + return new ModelAndView(getViewName(), RequestContextUtils.getInputFlashMap(request)); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/ParameterizableViewControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/ParameterizableViewControllerTests.java new file mode 100644 index 00000000000..77b90fb0b42 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/ParameterizableViewControllerTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2012 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.servlet.mvc; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.ui.ModelMap; +import org.springframework.web.servlet.FlashMapManager; +import org.springframework.web.servlet.ModelAndView; + +/** + * Test fixture with a ParameterizableViewController. + * + * @author Rossen Stoyanchev + * @since 3.1.1 + */ +public class ParameterizableViewControllerTests { + + private ParameterizableViewController controller; + + private MockHttpServletRequest request; + + @Before + public void setup() { + this.controller = new ParameterizableViewController(); + this.request = new MockHttpServletRequest("GET", "/"); + } + + @Test + public void handleRequestWithViewName() throws Exception { + String viewName = "testView"; + this.controller.setViewName(viewName); + ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse()); + assertEquals(viewName, mav.getViewName()); + assertTrue(mav.getModel().isEmpty()); + } + + @Test + public void handleRequestWithoutViewName() throws Exception { + ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse()); + assertNull(mav.getViewName()); + assertTrue(mav.getModel().isEmpty()); + } + + @Test + public void handleRequestWithFlashAttributes() throws Exception { + this.request.setAttribute(FlashMapManager.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value")); + ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse()); + assertEquals(1, mav.getModel().size()); + assertEquals("value", mav.getModel().get("name")); + } + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java index ed45d97f820..58258067f5a 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java @@ -20,8 +20,10 @@ import junit.framework.TestCase; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.ui.ModelMap; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; +import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.ModelAndView; @@ -150,6 +152,17 @@ public class UrlFilenameViewControllerTests extends TestCase { assertTrue(mv.getModel().isEmpty()); } + public void testWithFlashAttributes() throws Exception { + UrlFilenameViewController ctrl = new UrlFilenameViewController(); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index"); + request.setAttribute(FlashMapManager.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value")); + MockHttpServletResponse response = new MockHttpServletResponse(); + ModelAndView mv = ctrl.handleRequest(request, response); + assertEquals("index", mv.getViewName()); + assertEquals(1, mv.getModel().size()); + assertEquals("value", mv.getModel().get("name")); + } + private void exposePathInMapping(MockHttpServletRequest request, String mapping) { String pathInMapping = this.pathMatcher.extractPathWithinPattern(mapping, request.getRequestURI()); request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathInMapping); From e8fc90ce3e4554f14eaa86ce05591249d3fe62fa Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 13 Jan 2012 15:25:53 -0500 Subject: [PATCH 08/20] SPR-8917 Fix issue with quoted parameter values in MediaType. --- .../src/main/java/org/springframework/http/MediaType.java | 6 +++--- .../test/java/org/springframework/http/MediaTypeTests.java | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java index b2f8a800b07..d467e1feaeb 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/MediaType.java +++ b/org.springframework.web/src/main/java/org/springframework/http/MediaType.java @@ -331,7 +331,7 @@ public class MediaType implements Comparable { String attribute = entry.getKey(); String value = entry.getValue(); checkParameters(attribute, value); - m.put(attribute, unquote(value)); + m.put(attribute, value); } this.parameters = Collections.unmodifiableMap(m); } @@ -428,7 +428,7 @@ public class MediaType implements Comparable { */ public Charset getCharSet() { String charSet = getParameter(PARAM_CHARSET); - return (charSet != null ? Charset.forName(charSet) : null); + return (charSet != null ? Charset.forName(unquote(charSet)) : null); } /** @@ -438,7 +438,7 @@ public class MediaType implements Comparable { */ public double getQualityValue() { String qualityFactory = getParameter(PARAM_QUALITY_FACTOR); - return (qualityFactory != null ? Double.parseDouble(qualityFactory) : 1D); + return (qualityFactory != null ? Double.parseDouble(unquote(qualityFactory)) : 1D); } /** diff --git a/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java b/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java index 70bc486d602..5ed742a0eb2 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/MediaTypeTests.java @@ -173,9 +173,12 @@ public class MediaTypeTests { MediaType.parseMediaType("text/html; charset=foo-bar"); } + // SPR-8917 + @Test public void parseMediaTypeQuotedParameterValue() { - MediaType.parseMediaType("audio/*;attr=\"v>alue\""); + MediaType mediaType = MediaType.parseMediaType("audio/*;attr=\"v>alue\""); + assertEquals("\"v>alue\"", mediaType.getParameter("attr")); } @Test(expected = IllegalArgumentException.class) From b91e989ad4eb99999fc16944f37509b6bec06901 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 12 Jan 2012 17:48:40 +0100 Subject: [PATCH 09/20] JMS CachingConnectionFactory never caches consumers for temporary queues and topics --- .../jms/connection/CachingConnectionFactory.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java b/org.springframework.jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java index d2b2249e08b..35456ea4581 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -34,6 +34,8 @@ import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.QueueSession; import javax.jms.Session; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; import javax.jms.Topic; import javax.jms.TopicSession; @@ -323,16 +325,18 @@ public class CachingConnectionFactory extends SingleConnectionFactory { // let raw JMS invocation throw an exception if Destination (i.e. args[0]) is null if ((methodName.equals("createConsumer") || methodName.equals("createReceiver") || methodName.equals("createSubscriber"))) { - if (args[0] != null) { - return getCachedConsumer((Destination) args[0], + Destination dest = (Destination) args[0]; + if (dest != null && !(dest instanceof TemporaryQueue || dest instanceof TemporaryTopic)) { + return getCachedConsumer(dest, (args.length > 1 ? (String) args[1] : null), (args.length > 2 && (Boolean) args[2]), null); } } else if (methodName.equals("createDurableSubscriber")) { - if (args[0] != null) { - return getCachedConsumer((Destination) args[0], + Destination dest = (Destination) args[0]; + if (dest != null) { + return getCachedConsumer(dest, (args.length > 2 ? (String) args[2] : null), (args.length > 3 && (Boolean) args[3]), (String) args[1]); From 96b8c464808859943719551db2351d4b5502fc14 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 14:50:42 +0100 Subject: [PATCH 10/20] nicer error message indicating JAX-WS 2.1 requirement at runtime (SPR-8998) --- .../remoting/jaxws/JaxWsPortClientInterceptor.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/jaxws/JaxWsPortClientInterceptor.java b/org.springframework.web/src/main/java/org/springframework/remoting/jaxws/JaxWsPortClientInterceptor.java index f1d76fdb75a..47e8bd02903 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/jaxws/JaxWsPortClientInterceptor.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/jaxws/JaxWsPortClientInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -407,7 +407,13 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory */ protected Object getPortStub(Service service, QName portQName) { if (this.webServiceFeatures != null) { - return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures); + try { + return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures); + } + catch (LinkageError ex) { + throw new IllegalStateException( + "Specifying the 'webServiceFeatures' property requires JAX-WS 2.1 or higher at runtime", ex); + } } else { return (portQName != null ? service.getPort(portQName, getServiceInterface()) : @@ -527,6 +533,7 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory } } + /** * Inner class in order to avoid a hard-coded JAX-WS 2.1 dependency. * JAX-WS 2.0, as used in Java EE 5, didn't have WebServiceFeatures yet... From 56d0e67581eee82ba5ee67602dcc57d705e8264e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 15:38:50 +0100 Subject: [PATCH 11/20] added "getConfiguration" accessor to Hibernate 4 LocalSessionFactoryBean (SPR-8961) --- .../hibernate4/LocalSessionFactoryBean.java | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBean.java index 50e161d8f84..7c2ecf1e8b7 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -23,6 +23,7 @@ import javax.sql.DataSource; import org.hibernate.Interceptor; import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; import org.hibernate.cfg.NamingStrategy; import org.springframework.beans.factory.DisposableBean; @@ -37,16 +38,15 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; /** - * {@link org.springframework.beans.factory.FactoryBean} that creates a - * Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to - * set up a shared Hibernate SessionFactory in a Spring application context; - * the SessionFactory can then be passed to Hibernate-based DAOs via - * dependency injection. + * {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate + * {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared + * Hibernate SessionFactory in a Spring application context; the SessionFactory can + * then be passed to Hibernate-based data access objects via dependency injection. * - *

      NOTE: This variant of LocalSessionFactoryBean requires Hibernate 4.0 - * or higher. It is similar in role to the same-named class in the orm.hibernate3 - * package. However, in practice, it is closer to AnnotationSessionFactoryBean - * since its core purpose is to bootstrap a SessionFactory from annotation scanning. + *

      NOTE: This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher. + * It is similar in role to the same-named class in the orm.hibernate3 package. + * However, in practice, it is closer to AnnotationSessionFactoryBean since + * its core purpose is to bootstrap a SessionFactory from annotation scanning. * * @author Juergen Hoeller * @since 3.1 @@ -84,6 +84,8 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + private Configuration configuration; + private SessionFactory sessionFactory; @@ -328,7 +330,36 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator sfb.scanPackages(this.packagesToScan); } - this.sessionFactory = sfb.buildSessionFactory(); + // Build SessionFactory instance. + this.configuration = sfb; + this.sessionFactory = buildSessionFactory(sfb); + } + + /** + * Subclasses can override this method to perform custom initialization + * of the SessionFactory instance, creating it via the given Configuration + * object that got prepared by this LocalSessionFactoryBean. + *

      The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory. + * A custom implementation could prepare the instance in a specific way (e.g. applying + * a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass. + * @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean + * @return the SessionFactory instance + * @see LocalSessionFactoryBuilder#buildSessionFactory + */ + protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { + return sfb.buildSessionFactory(); + } + + /** + * Return the Hibernate Configuration object used to build the SessionFactory. + * Allows for access to configuration metadata stored there (rarely needed). + * @throws IllegalStateException if the Configuration object has not been initialized yet + */ + public final Configuration getConfiguration() { + if (this.configuration == null) { + throw new IllegalStateException("Configuration not initialized yet"); + } + return this.configuration; } From 4a63a5b3bafcd7387846b8715ff6e60a425377c5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 15:39:24 +0100 Subject: [PATCH 12/20] fixed "configTimeRegionFactoryHolder" reset --- .../orm/hibernate3/LocalSessionFactoryBean.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java index 0cb0d7335f1..887ceaed812 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/LocalSessionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -787,7 +787,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen configTimeTransactionManagerHolder.remove(); } if (this.cacheRegionFactory != null) { - configTimeCacheProviderHolder.remove(); + configTimeRegionFactoryHolder.remove(); } if (this.cacheProvider != null) { configTimeCacheProviderHolder.remove(); @@ -862,7 +862,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen /** * Return the Configuration object used to build the SessionFactory. - * Allows access to configuration metadata stored there (rarely needed). + * Allows for access to configuration metadata stored there (rarely needed). * @throws IllegalStateException if the Configuration object has not been initialized yet */ public final Configuration getConfiguration() { From 3fdbe1081d134c7279ae2f570c7487aae68ddc36 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 19:53:40 +0100 Subject: [PATCH 13/20] JMS SimpleMessageListenerContainer silently falls back to lazy registration of consumers () --- .../SimpleMessageListenerContainer.java | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java b/org.springframework.jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java index a47d1f99e21..82d953564b1 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -62,6 +62,8 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta private boolean pubSubNoLocal = false; + private boolean connectLazily = false; + private int concurrentConsumers = 1; private Executor taskExecutor; @@ -89,6 +91,20 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta return this.pubSubNoLocal; } + /** + * Specify whether to connect lazily, i.e. whether to establish the JMS Connection + * and the corresponding Sessions and MessageConsumers as late as possible - + * in the start phase of this container. + *

      Default is "false": connecting early, i.e. during the bean initialization phase. + * Set this flag to "true" in order to switch to lazy connecting if your target broker + * is likely to not have started up yet and you prefer to not even try a connection. + * @see #start() + * @see #initialize() + */ + public void setConnectLazily(boolean connectLazily) { + this.connectLazily = connectLazily; + } + /** * Specify concurrency limits via a "lower-upper" String, e.g. "5-10", or a simple * upper limit String, e.g. "10". @@ -159,6 +175,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta this.taskExecutor = taskExecutor; } + @Override protected void validateConfiguration() { super.validateConfiguration(); if (isSubscriptionDurable() && this.concurrentConsumers != 1) { @@ -174,6 +191,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta /** * Always use a shared JMS Connection. */ + @Override protected final boolean sharedConnectionEnabled() { return true; } @@ -183,15 +201,25 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta * in the form of a JMS Session plus associated MessageConsumer. * @see #createListenerConsumer */ + @Override protected void doInitialize() throws JMSException { - establishSharedConnection(); - initializeConsumers(); + if (!this.connectLazily) { + try { + establishSharedConnection(); + } + catch (JMSException ex) { + logger.debug("Could not connect on initialization - registering message consumers lazily", ex); + return; + } + initializeConsumers(); + } } /** * Re-initializes this container's JMS message consumers, * if not initialized already. */ + @Override protected void doStart() throws JMSException { super.doStart(); initializeConsumers(); @@ -200,6 +228,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta /** * Registers this listener container as JMS ExceptionListener on the shared connection. */ + @Override protected void prepareSharedConnection(Connection connection) throws JMSException { super.prepareSharedConnection(connection); connection.setExceptionListener(this); @@ -320,6 +349,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta /** * Destroy the registered JMS Sessions and associated MessageConsumers. */ + @Override protected void doShutdown() throws JMSException { logger.debug("Closing JMS MessageConsumers"); for (MessageConsumer consumer : this.consumers) { From 697bc43c406f3fd2dcddbdb47c1e03cf55182970 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 19:54:10 +0100 Subject: [PATCH 14/20] added missing @Override annotations --- .../listener/AbstractJmsListeningContainer.java | 3 ++- .../listener/AbstractMessageListenerContainer.java | 3 ++- .../AbstractPollingMessageListenerContainer.java | 8 +++++--- .../listener/DefaultMessageListenerContainer.java | 14 +++++++++++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java index d6303305fe0..6a2dc5e0d43 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -151,6 +151,7 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess /** * Delegates to {@link #validateConfiguration()} and {@link #initialize()}. */ + @Override public void afterPropertiesSet() { super.afterPropertiesSet(); validateConfiguration(); diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java index 21efc557e7b..b735a6632f0 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -410,6 +410,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen return this.acceptMessagesWhileStopping; } + @Override protected void validateConfiguration() { if (this.destination == null) { throw new IllegalArgumentException("Property 'destination' or 'destinationName' is required"); diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java index ac7e0e06a32..566e3f9de99 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -75,8 +75,7 @@ import org.springframework.transaction.support.TransactionSynchronizationUtils; * @see #receiveAndExecute * @see #setTransactionManager */ -public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer - implements BeanNameAware { +public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer { /** * The default receive timeout: 1000 ms = 1 second. @@ -100,6 +99,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe private volatile Boolean commitAfterNoMessageReceived; + @Override public void setSessionTransacted(boolean sessionTransacted) { super.setSessionTransacted(sessionTransacted); this.sessionTransactedCalled = true; @@ -188,6 +188,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe } + @Override public void initialize() { // Set sessionTransacted=true in case of a non-JTA transaction manager. if (!this.sessionTransactedCalled && @@ -374,6 +375,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe * container's "sessionTransacted" flag being set to "true". * @see org.springframework.jms.connection.JmsResourceHolder */ + @Override protected boolean isSessionLocallyTransacted(Session session) { if (!super.isSessionLocallyTransacted(session)) { return false; diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/org.springframework.jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index 7e12561fe6a..a2d8dfabfdb 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -470,6 +470,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe } } + @Override protected void validateConfiguration() { super.validateConfiguration(); synchronized (this.lifecycleMonitor) { @@ -484,6 +485,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe // Implementation of AbstractMessageListenerContainer's template methods //------------------------------------------------------------------------- + @Override public void initialize() { // Adapt default cache level. if (this.cacheLevel == CACHE_AUTO) { @@ -516,6 +518,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * @see #scheduleNewInvoker * @see #setTaskExecutor */ + @Override protected void doInitialize() throws JMSException { synchronized (this.lifecycleMonitor) { for (int i = 0; i < this.concurrentConsumers; i++) { @@ -527,6 +530,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Destroy the registered JMS Sessions and associated MessageConsumers. */ + @Override protected void doShutdown() throws JMSException { logger.debug("Waiting for shutdown of message listener invokers"); try { @@ -549,6 +553,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Overridden to reset the stop callback, if any. */ + @Override public void start() throws JmsException { synchronized (this.lifecycleMonitor) { this.stopCallback = null; @@ -658,6 +663,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * @see #setCacheLevel * @see #CACHE_CONNECTION */ + @Override protected final boolean sharedConnectionEnabled() { return (getCacheLevel() >= CACHE_CONNECTION); } @@ -666,6 +672,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * Re-executes the given task via this listener container's TaskExecutor. * @see #setTaskExecutor */ + @Override protected void doRescheduleTask(Object task) { this.taskExecutor.execute((Runnable) task); } @@ -674,6 +681,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * Tries scheduling a new invoker, since we know messages are coming in... * @see #scheduleNewInvokerIfAppropriate() */ + @Override protected void messageReceived(Object invoker, Session session) { ((AsyncMessageListenerInvoker) invoker).setIdle(false); scheduleNewInvokerIfAppropriate(); @@ -682,6 +690,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Marks the affected invoker as idle. */ + @Override protected void noMessageReceived(Object invoker, Session session) { ((AsyncMessageListenerInvoker) invoker).setIdle(true); } @@ -745,6 +754,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * asynchronous invokers to establish the shared Connection on first access. * @see #refreshConnectionUntilSuccessful() */ + @Override protected void establishSharedConnection() { try { super.establishSharedConnection(); @@ -760,6 +770,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * Connection.start(), relying on listeners to perform * appropriate recovery. */ + @Override protected void startSharedConnection() { try { super.startSharedConnection(); @@ -774,6 +785,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * Connection.stop(), relying on listeners to perform * appropriate recovery after a restart. */ + @Override protected void stopSharedConnection() { try { super.stopSharedConnection(); From 958bd49850ad6d0a182ff5295f4d40e7d840baa9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 15 Jan 2012 20:05:57 +0100 Subject: [PATCH 15/20] Hibernate 4 LocalSessionFactoryBean, etc --- build-spring-framework/resources/changelog.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index 231d287b01b..827ad75bddb 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -6,7 +6,7 @@ http://www.springsource.org Changes in version 3.1.1 (2012-02-06) ------------------------------------- -* official support for Hibernate 4.0 GA +* official support for Hibernate 4.0 GA (as released in the meantime) * JBossNativeJdbcExtractor is compatible with JBoss AS 7 as well * context:property-placeholder's "file-encoding" attribute value is being applied correctly * DataBinder correctly handles ParseException from Formatter for String->String case @@ -19,8 +19,11 @@ Changes in version 3.1.1 (2012-02-06) * Hibernate 4 LocalSessionFactoryBean implements PersistenceExceptionTranslator interface as well * Hibernate 4 LocalSessionFactoryBean does not insist on a "dataSource" reference being set * added "entityInterceptor" property to Hibernate 4 LocalSessionFactoryBean +* added "getConfiguration" accessor to Hibernate 4 LocalSessionFactoryBean * corrected fix for QuartzJobBean to work with Quartz 2.0/2.1 -* Make AbstractHandlerMethodExceptionResolver an abstract class +* JMS CachingConnectionFactory never caches consumers for temporary queues and topics +* JMS SimpleMessageListenerContainer silently falls back to lazy registration of consumers + Changes in version 3.1 GA (2011-12-12) -------------------------------------- From 9c45acd43abefd5c7e004dfa51324b0c78ea2af9 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 16 Jan 2012 22:16:43 +0100 Subject: [PATCH 16/20] Update links to reference and api documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5aa077745ea..d4549d3a5fb 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Instructions on via Maven and other build systems are available via the project wiki. ## Documentation -See the current [Javadoc](http://static.springsource.org/spring/docs/current/javadoc-api) -and [Reference docs](http://static.springsource.org/spring/docs/current/spring-framework-reference). +See the current [Javadoc](http://static.springsource.org/spring-framework/docs/current/api) +and [Reference docs](http://static.springsource.org/spring-framework/docs/current/reference). ## Getting support Check out the [Spring forums](http://forum.springsource.org) and the From bcd8355e61ed784346d4d5259ba8bfb28ea30313 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 19 Jan 2012 23:47:31 -0500 Subject: [PATCH 17/20] SPR-8974 Fix regression in UriUtils.java --- .../resources/changelog.txt | 5 +- .../springframework/web/util/UriUtils.java | 125 ++++++++++++++++-- .../web/util/UriUtilsTests.java | 3 + 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index 827ad75bddb..eff2824e93e 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -23,7 +23,10 @@ Changes in version 3.1.1 (2012-02-06) * corrected fix for QuartzJobBean to work with Quartz 2.0/2.1 * JMS CachingConnectionFactory never caches consumers for temporary queues and topics * JMS SimpleMessageListenerContainer silently falls back to lazy registration of consumers - +* fix regresion in UriUtils +* allow adding flash attributes in methods with a ModelAndView return value +* preserve quotes in MediaType parameters +* make flash attributes available in the model of ParameterizableViewController and UrlFilenameViewController Changes in version 3.1 GA (2011-12-12) -------------------------------------- diff --git a/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java b/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java index e24781b745b..4d6c2a44997 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java +++ b/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java @@ -18,6 +18,8 @@ package org.springframework.web.util; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.springframework.util.Assert; @@ -38,38 +40,107 @@ import org.springframework.util.Assert; */ public abstract class UriUtils { + private static final String SCHEME_PATTERN = "([^:/?#]+):"; + + private static final String HTTP_PATTERN = "(http|https):"; + + private static final String USERINFO_PATTERN = "([^@/]*)"; + + private static final String HOST_PATTERN = "([^/?#:]*)"; + + private static final String PORT_PATTERN = "(\\d*)"; + + private static final String PATH_PATTERN = "([^?#]*)"; + + private static final String QUERY_PATTERN = "([^#]*)"; + + private static final String LAST_PATTERN = "(.*)"; + + // Regex patterns that matches URIs. See RFC 3986, appendix B + private static final Pattern URI_PATTERN = Pattern.compile( + "^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + + ")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?"); + + private static final Pattern HTTP_URL_PATTERN = Pattern.compile( + "^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" + + PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?"); // encoding /** * Encodes the given source URI into an encoded String. All various URI components are * encoded according to their respective valid character sets. + *

      Note that this method does not attempt to encode "=" and "&" + * characters in query parameter names and query parameter values because they cannot + * be parsed in a reliable way. Instead use: + *

      +	 *  UriComponents uriComponents = UriComponentsBuilder.fromUri("/path?name={value}").buildAndExpand("a=b");
      +	 *  String encodedUri = uriComponents.encode().toUriString();
      +	 * 
      * @param uri the URI to be encoded * @param encoding the character encoding to encode to * @return the encoded URI * @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws UnsupportedEncodingException when the given encoding parameter is not supported + * @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding */ public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException { - UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).build(); - UriComponents encoded = uriComponents.encode(encoding); - return encoded.toUriString(); - } + Assert.notNull(uri, "'uri' must not be null"); + Assert.hasLength(encoding, "'encoding' must not be empty"); + Matcher m = URI_PATTERN.matcher(uri); + if (m.matches()) { + String scheme = m.group(2); + String authority = m.group(3); + String userinfo = m.group(5); + String host = m.group(6); + String port = m.group(8); + String path = m.group(9); + String query = m.group(11); + String fragment = m.group(13); + + return encodeUriComponents(scheme, authority, userinfo, host, port, path, query, fragment, encoding); + } + else { + throw new IllegalArgumentException("[" + uri + "] is not a valid URI"); + } + } /** * Encodes the given HTTP URI into an encoded String. All various URI components are * encoded according to their respective valid character sets. *

      Note that this method does not support fragments ({@code #}), * as these are not supposed to be sent to the server, but retained by the client. + *

      Note that this method does not attempt to encode "=" and "&" + * characters in query parameter names and query parameter values because they cannot + * be parsed in a reliable way. Instead use: + *

      +	 *  UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("/path?name={value}").buildAndExpand("a=b");
      +	 *  String encodedUri = uriComponents.encode().toUriString();
      +	 * 
      * @param httpUrl the HTTP URL to be encoded * @param encoding the character encoding to encode to * @return the encoded URL * @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws UnsupportedEncodingException when the given encoding parameter is not supported + * @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding */ public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException { - UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(httpUrl).build(); - UriComponents encoded = uriComponents.encode(encoding); - return encoded.toUriString(); + Assert.notNull(httpUrl, "'httpUrl' must not be null"); + Assert.hasLength(encoding, "'encoding' must not be empty"); + Matcher m = HTTP_URL_PATTERN.matcher(httpUrl); + if (m.matches()) { + String scheme = m.group(1); + String authority = m.group(2); + String userinfo = m.group(4); + String host = m.group(5); + String portString = m.group(7); + String path = m.group(8); + String query = m.group(10); + + return encodeUriComponents(scheme, authority, userinfo, host, portString, path, query, null, encoding); + } + else { + throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL"); + } } /** @@ -87,20 +158,48 @@ public abstract class UriUtils { * @return the encoded URI * @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws UnsupportedEncodingException when the given encoding parameter is not supported + * @deprecated in favor of {@link UriComponentsBuilder} */ public static String encodeUriComponents(String scheme, String authority, String userInfo, String host, String port, String path, String query, String fragment, String encoding) throws UnsupportedEncodingException { - int portAsInt = (port != null ? Integer.parseInt(port) : -1); + Assert.hasLength(encoding, "'encoding' must not be empty"); + StringBuilder sb = new StringBuilder(); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); - builder.scheme(scheme).userInfo(userInfo).host(host).port(portAsInt); - builder.path(path).query(query).fragment(fragment); + if (scheme != null) { + sb.append(encodeScheme(scheme, encoding)); + sb.append(':'); + } - UriComponents encoded = builder.build().encode(encoding); + if (authority != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(encodeUserInfo(userInfo, encoding)); + sb.append('@'); + } + if (host != null) { + sb.append(encodeHost(host, encoding)); + } + if (port != null) { + sb.append(':'); + sb.append(encodePort(port, encoding)); + } + } - return encoded.toUriString(); + sb.append(encodePath(path, encoding)); + + if (query != null) { + sb.append('?'); + sb.append(encodeQuery(query, encoding)); + } + + if (fragment != null) { + sb.append('#'); + sb.append(encodeFragment(fragment, encoding)); + } + + return sb.toString(); } diff --git a/org.springframework.web/src/test/java/org/springframework/web/util/UriUtilsTests.java b/org.springframework.web/src/test/java/org/springframework/web/util/UriUtilsTests.java index 4ccd215f140..444b7ec43c2 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/util/UriUtilsTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/util/UriUtilsTests.java @@ -128,6 +128,9 @@ public class UriUtilsTests { assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar", UriUtils.encodeUri("http://example.com/query=foo@bar", ENC)); + // SPR-8974 + assertEquals("http://example.org?format=json&url=http://another.com?foo=bar", + UriUtils.encodeUri("http://example.org?format=json&url=http://another.com?foo=bar", ENC)); } @Test From 79f32c7f33489bf9e746928d9e33fb29b53e0a96 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 20 Jan 2012 16:09:00 +0100 Subject: [PATCH 18/20] SPR-8986 Add the ability to Scan Packages for JAXB Marshalling Jaxb2Marshaller now has the capability to scan for classes annotated with JAXB2 annotations. --- .../oxm/jaxb/ClassPathJaxb2TypeScanner.java | 120 ++++++++++++++++++ .../oxm/jaxb/Jaxb2Marshaller.java | 73 ++++++++++- .../oxm/jaxb/Jaxb2MarshallerTests.java | 9 +- org.springframework.oxm/template.mf | 1 + 4 files changed, 195 insertions(+), 8 deletions(-) create mode 100644 org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/ClassPathJaxb2TypeScanner.java diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/ClassPathJaxb2TypeScanner.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/ClassPathJaxb2TypeScanner.java new file mode 100644 index 00000000000..c6f97d2493c --- /dev/null +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/ClassPathJaxb2TypeScanner.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2012 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.oxm.jaxb; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlType; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternUtils; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.oxm.UncategorizedMappingException; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * Helper class for {@link Jaxb2Marshaller} that scans given packages for classes marked with JAXB2 annotations. + * + * @author Arjen Poutsma + * @author David Harrigan + * @see #scanPackages() + */ +class ClassPathJaxb2TypeScanner { + + private static final String RESOURCE_PATTERN = "/**/*.class"; + + private final TypeFilter[] jaxb2TypeFilters = + new TypeFilter[]{new AnnotationTypeFilter(XmlRootElement.class, false), + new AnnotationTypeFilter(XmlType.class, false), new AnnotationTypeFilter(XmlSeeAlso.class, false), + new AnnotationTypeFilter(XmlEnum.class, false)}; + + private final String[] packagesToScan; + + private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + + private List> jaxb2Classes = new ArrayList>(); + + /** Constructs a new {@code ClassPathJaxb2TypeScanner} for the given packages. */ + ClassPathJaxb2TypeScanner(String[] packagesToScan) { + Assert.notEmpty(packagesToScan, "'packagesToScan' must not be empty"); + this.packagesToScan = packagesToScan; + } + + void setResourceLoader(ResourceLoader resourceLoader) { + if (resourceLoader != null) { + this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); + } + } + + /** Returns the JAXB2 classes found in the specified packages. */ + Class[] getJaxb2Classes() { + return jaxb2Classes.toArray(new Class[jaxb2Classes.size()]); + } + + /** + * Scans the packages for classes marked with JAXB2 annotations. + * + * @throws UncategorizedMappingException in case of errors + */ + void scanPackages() throws UncategorizedMappingException { + try { + for (String packageToScan : packagesToScan) { + String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(packageToScan) + RESOURCE_PATTERN; + Resource[] resources = resourcePatternResolver.getResources(pattern); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver); + for (Resource resource : resources) { + MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); + if (isJaxb2Class(metadataReader, metadataReaderFactory)) { + String className = metadataReader.getClassMetadata().getClassName(); + Class jaxb2AnnotatedClass = resourcePatternResolver.getClassLoader().loadClass(className); + jaxb2Classes.add(jaxb2AnnotatedClass); + } + } + } + } + catch (IOException ex) { + throw new UncategorizedMappingException("Failed to scan classpath for unlisted classes", ex); + } + catch (ClassNotFoundException ex) { + throw new UncategorizedMappingException("Failed to load annotated classes from classpath", ex); + } + } + + private boolean isJaxb2Class(MetadataReader reader, MetadataReaderFactory factory) throws IOException { + for (TypeFilter filter : jaxb2TypeFilters) { + if (filter.match(reader, factory)) { + return true; + } + } + return false; + } + + +} diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index d97b393a035..e16ba7e6e95 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -16,7 +16,7 @@ package org.springframework.oxm.jaxb; -import java.awt.Image; +import java.awt.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -75,8 +75,10 @@ import org.xml.sax.helpers.XMLReaderFactory; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ResourceLoaderAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.oxm.GenericMarshaller; import org.springframework.oxm.GenericUnmarshaller; import org.springframework.oxm.MarshallingFailureException; @@ -117,7 +119,7 @@ import org.springframework.util.xml.StaxUtils; */ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware, - InitializingBean { + ResourceLoaderAware, InitializingBean { private static final String CID = "cid:"; @@ -130,6 +132,8 @@ public class Jaxb2Marshaller private String contextPath; private Class[] classesToBeBound; + + private String[] packagesToScan; private Map jaxbContextProperties; @@ -153,6 +157,8 @@ public class Jaxb2Marshaller private ClassLoader beanClassLoader; + private ResourceLoader resourceLoader; + private JAXBContext jaxbContext; private Schema schema; @@ -175,6 +181,8 @@ public class Jaxb2Marshaller /** * Set a JAXB context path. + *

      Setting this property, {@link #setClassesToBeBound "classesToBeBound"}, or + * {@link #setPackagesToScan "packagesToScan"} is required. */ public void setContextPath(String contextPath) { Assert.hasText(contextPath, "'contextPath' must not be null"); @@ -190,7 +198,8 @@ public class Jaxb2Marshaller /** * Set the list of Java classes to be recognized by a newly created JAXBContext. - * Setting this property or {@link #setContextPath "contextPath"} is required. + *

      Setting this property, {@link #setContextPath "contextPath"}, or + * {@link #setPackagesToScan "packagesToScan"} is required. */ public void setClassesToBeBound(Class... classesToBeBound) { Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty"); @@ -204,6 +213,23 @@ public class Jaxb2Marshaller return this.classesToBeBound; } + /** + * Set the packages to search using Spring-based scanning for classes with JAXB2 annotations in the classpath. + *

      Setting this property, {@link #setContextPath "contextPath"}, or + * {@link #setClassesToBeBound "classesToBeBound"} is required. This is analogous to Spring's component-scan feature + * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). + */ + public void setPackagesToScan(String[] packagesToScan) { + this.packagesToScan = packagesToScan; + } + + /** + * Returns the packages to search for JAXB2 annotations. + */ + public String[] getPackagesToScan() { + return packagesToScan; + } + /** * Set the JAXBContext properties. These implementation-specific * properties will be set on the underlying JAXBContext. @@ -337,13 +363,23 @@ public class Jaxb2Marshaller this.beanClassLoader = classLoader; } + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } public final void afterPropertiesSet() throws Exception { - if (StringUtils.hasLength(getContextPath()) && !ObjectUtils.isEmpty(getClassesToBeBound())) { - throw new IllegalArgumentException("Specify either 'contextPath' or 'classesToBeBound property'; not both"); + boolean hasContextPath = StringUtils.hasLength(getContextPath()); + boolean hasClassesToBeBound = !ObjectUtils.isEmpty(getClassesToBeBound()); + boolean hasPackagesToScan = !ObjectUtils.isEmpty(getPackagesToScan()); + + if (hasContextPath && (hasClassesToBeBound || hasPackagesToScan) || + (hasClassesToBeBound && hasPackagesToScan)) { + throw new IllegalArgumentException("Specify either 'contextPath', 'classesToBeBound', " + + "or 'packagesToScan'"); } - else if (!StringUtils.hasLength(getContextPath()) && ObjectUtils.isEmpty(getClassesToBeBound())) { - throw new IllegalArgumentException("Setting either 'contextPath' or 'classesToBeBound' is required"); + if (!hasContextPath && !hasClassesToBeBound && !hasPackagesToScan) { + throw new IllegalArgumentException( + "Setting either 'contextPath', 'classesToBeBound', " + "or 'packagesToScan' is required"); } if (!this.lazyInit) { getJaxbContext(); @@ -362,6 +398,9 @@ public class Jaxb2Marshaller else if (!ObjectUtils.isEmpty(getClassesToBeBound())) { this.jaxbContext = createJaxbContextFromClasses(); } + else if (!ObjectUtils.isEmpty(getPackagesToScan())) { + this.jaxbContext = createJaxbContextFromPackages(); + } } catch (JAXBException ex) { throw convertJaxbException(ex); @@ -405,6 +444,26 @@ public class Jaxb2Marshaller } } + private JAXBContext createJaxbContextFromPackages() throws JAXBException { + if (logger.isInfoEnabled()) { + logger.info("Creating JAXBContext by scanning packages [" + + StringUtils.arrayToCommaDelimitedString(getPackagesToScan()) + "]"); + } + ClassPathJaxb2TypeScanner scanner = new ClassPathJaxb2TypeScanner(getPackagesToScan()); + scanner.setResourceLoader(this.resourceLoader); + scanner.scanPackages(); + Class[] jaxb2Classes = scanner.getJaxb2Classes(); + if (logger.isDebugEnabled()) { + logger.debug("Found JAXB2 classes: [" + StringUtils.arrayToCommaDelimitedString(jaxb2Classes) + "]"); + } + if (this.jaxbContextProperties != null) { + return JAXBContext.newInstance(jaxb2Classes, this.jaxbContextProperties); + } + else { + return JAXBContext.newInstance(jaxb2Classes); + } + } + private Schema loadSchema(Resource[] resources, String schemaLanguage) throws IOException, SAXException { if (logger.isDebugEnabled()) { logger.debug("Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(this.schemaResources)); diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java index 8e743037072..63af36289fa 100644 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -279,6 +279,13 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests { assertTrue("No XML written", writer.toString().length() > 0); } + @Test + public void supportsPackagesToScan() throws Exception { + marshaller = new Jaxb2Marshaller(); + marshaller.setPackagesToScan(new String[] {CONTEXT_PATH}); + marshaller.afterPropertiesSet(); + } + @XmlRootElement public static class DummyRootElement { diff --git a/org.springframework.oxm/template.mf b/org.springframework.oxm/template.mf index 5cf0002e593..221f362d580 100644 --- a/org.springframework.oxm/template.mf +++ b/org.springframework.oxm/template.mf @@ -12,6 +12,7 @@ Import-Template: org.exolab.castor.*;version="[1.2.0, 2.0.0)";resolution:=optional, org.jibx.runtime.*;version="[1.1.5, 2.0.0)";resolution:=optional, org.springframework.beans.*;version=${spring.osgi.range}, + org.springframework.context.*;version=${spring.osgi.range}, org.springframework.core.*;version=${spring.osgi.range}, org.springframework.util.*;version=${spring.osgi.range}, org.w3c.dom.*;version="0", From ff9ad7adc603fd395c212b7329a2b0b2804a82bc Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 23 Jan 2012 14:03:14 +0100 Subject: [PATCH 19/20] SPR-8986 RestTemplate throws IllegalArgumentException when HTTP status is not in the HttpStatus enum - Added status codes from Wikipedia --- .../org/springframework/http/HttpStatus.java | 57 +++++++- .../springframework/http/HttpStatusTests.java | 122 ++++++++++++++---- 2 files changed, 149 insertions(+), 30 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/http/HttpStatus.java b/org.springframework.web/src/main/java/org/springframework/http/HttpStatus.java index 79a2bbd00c8..1de14c2884a 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/HttpStatus.java +++ b/org.springframework.web/src/main/java/org/springframework/http/HttpStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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 @@ package org.springframework.http; * @author Arjen Poutsma * @see HttpStatus.Series * @see HTTP Status Code Registry + * @see List of HTTP status codes - Wikipedia */ public enum HttpStatus { @@ -44,6 +45,12 @@ public enum HttpStatus { * @see WebDAV */ PROCESSING(102, "Processing"), + /** + * {@code 103 Checkpoint}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, "Checkpoint"), // 2xx Success @@ -140,6 +147,12 @@ public enum HttpStatus { * @see HTTP/1.1 */ TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Resume Incomplete}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + RESUME_INCOMPLETE(308, "Resume Incomplete"), // --- 4xx Client Error --- @@ -187,7 +200,7 @@ public enum HttpStatus { * {@code 408 Request Timeout}. * @see HTTP/1.1 */ - REQUEST_TIMEOUT(408, "Request Time-out"), + REQUEST_TIMEOUT(408, "Request Timeout"), /** * {@code 409 Conflict}. * @see HTTP/1.1 @@ -217,7 +230,7 @@ public enum HttpStatus { * {@code 414 Request-URI Too Long}. * @see HTTP/1.1 */ - REQUEST_URI_TOO_LONG(414, "Request-URI Too Large"), + REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), /** * {@code 415 Unsupported Media Type}. * @see HTTP/1.1 @@ -233,6 +246,11 @@ public enum HttpStatus { * @see HTTP/1.1 */ EXPECTATION_FAILED(417, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, "I'm a teapot"), /** * {@code 419 Insufficient Space on Resource}. * @see WebDAV Draft @@ -268,6 +286,24 @@ public enum HttpStatus { * @see Upgrading to TLS Within HTTP/1.1 */ UPGRADE_REQUIRED(426, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * @see Additional HTTP Status + * Codes + */ + PRECONDITION_REQUIRED(428, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status + * Codes + */ + TOO_MANY_REQUESTS(429, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * @see Additional HTTP Status + * Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), // --- 5xx Server Error --- @@ -295,7 +331,7 @@ public enum HttpStatus { * {@code 504 Gateway Timeout}. * @see HTTP/1.1 */ - GATEWAY_TIMEOUT(504, "Gateway Time-out"), + GATEWAY_TIMEOUT(504, "Gateway Timeout"), /** * {@code 505 HTTP Version Not Supported}. * @see HTTP/1.1 @@ -316,11 +352,22 @@ public enum HttpStatus { * @see WebDAV Binding Extensions */ LOOP_DETECTED(508, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), /** * {@code 510 Not Extended} * @see HTTP Extension Framework */ - NOT_EXTENDED(510, "Not Extended"); + NOT_EXTENDED(510, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * @see Additional HTTP Status + * Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + private final int value; diff --git a/org.springframework.web/src/test/java/org/springframework/http/HttpStatusTests.java b/org.springframework.web/src/test/java/org/springframework/http/HttpStatusTests.java index 4fbe093e769..15fd5474028 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/HttpStatusTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/HttpStatusTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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,38 +16,110 @@ package org.springframework.http; -import static org.junit.Assert.*; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.*; + /** @author Arjen Poutsma */ public class HttpStatusTests { - private int[] registryValues = - new int[]{100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, - 307, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 422, - 423, 424, 426, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510,}; + private Map statusCodes = new LinkedHashMap(); - private String[] registryDescriptions = - new String[]{"CONTINUE", "SWITCHING_PROTOCOLS", "PROCESSING", "OK", "CREATED", "ACCEPTED", - "NON_AUTHORITATIVE_INFORMATION", "NO_CONTENT", "RESET_CONTENT", "PARTIAL_CONTENT", "MULTI_STATUS", - "ALREADY_REPORTED", "IM_USED", "MULTIPLE_CHOICES", "MOVED_PERMANENTLY", "FOUND", "SEE_OTHER", - "NOT_MODIFIED", "USE_PROXY", "TEMPORARY_REDIRECT", "BAD_REQUEST", "UNAUTHORIZED", - "PAYMENT_REQUIRED", "FORBIDDEN", "NOT_FOUND", "METHOD_NOT_ALLOWED", "NOT_ACCEPTABLE", - "PROXY_AUTHENTICATION_REQUIRED", "REQUEST_TIMEOUT", "CONFLICT", "GONE", "LENGTH_REQUIRED", - "PRECONDITION_FAILED", "REQUEST_ENTITY_TOO_LARGE", "REQUEST_URI_TOO_LONG", "UNSUPPORTED_MEDIA_TYPE", - "REQUESTED_RANGE_NOT_SATISFIABLE", "EXPECTATION_FAILED", "UNPROCESSABLE_ENTITY", "LOCKED", - "FAILED_DEPENDENCY", "UPGRADE_REQUIRED", "INTERNAL_SERVER_ERROR", "NOT_IMPLEMENTED", "BAD_GATEWAY", - "SERVICE_UNAVAILABLE", "GATEWAY_TIMEOUT", "HTTP_VERSION_NOT_SUPPORTED", "VARIANT_ALSO_NEGOTIATES", - "INSUFFICIENT_STORAGE", "LOOP_DETECTED", "NOT_EXTENDED",}; + @Before + public void createStatusCodes() { + statusCodes.put(100, "CONTINUE"); + statusCodes.put(101, "SWITCHING_PROTOCOLS"); + statusCodes.put(102, "PROCESSING"); + statusCodes.put(103, "CHECKPOINT"); - @Test - public void registryValues() { - for (int i = 0; i < registryValues.length; i++) { - HttpStatus status = HttpStatus.valueOf(registryValues[i]); - assertEquals("Invalid value", registryValues[i], status.value()); - assertEquals("Invalid descripion", registryDescriptions[i], status.name()); - } + statusCodes.put(200, "OK"); + statusCodes.put(201, "CREATED"); + statusCodes.put(202, "ACCEPTED"); + statusCodes.put(203, "NON_AUTHORITATIVE_INFORMATION"); + statusCodes.put(204, "NO_CONTENT"); + statusCodes.put(205, "RESET_CONTENT"); + statusCodes.put(206, "PARTIAL_CONTENT"); + statusCodes.put(207, "MULTI_STATUS"); + statusCodes.put(208, "ALREADY_REPORTED"); + statusCodes.put(226, "IM_USED"); + statusCodes.put(300, "MULTIPLE_CHOICES"); + statusCodes.put(301, "MOVED_PERMANENTLY"); + statusCodes.put(302, "FOUND"); + statusCodes.put(303, "SEE_OTHER"); + statusCodes.put(304, "NOT_MODIFIED"); + statusCodes.put(305, "USE_PROXY"); + statusCodes.put(307, "TEMPORARY_REDIRECT"); + statusCodes.put(308, "RESUME_INCOMPLETE"); + + statusCodes.put(400, "BAD_REQUEST"); + statusCodes.put(401, "UNAUTHORIZED"); + statusCodes.put(402, "PAYMENT_REQUIRED"); + statusCodes.put(403, "FORBIDDEN"); + statusCodes.put(404, "NOT_FOUND"); + statusCodes.put(405, "METHOD_NOT_ALLOWED"); + statusCodes.put(406, "NOT_ACCEPTABLE"); + statusCodes.put(407, "PROXY_AUTHENTICATION_REQUIRED"); + statusCodes.put(408, "REQUEST_TIMEOUT"); + statusCodes.put(409, "CONFLICT"); + statusCodes.put(410, "GONE"); + statusCodes.put(411, "LENGTH_REQUIRED"); + statusCodes.put(412, "PRECONDITION_FAILED"); + statusCodes.put(413, "REQUEST_ENTITY_TOO_LARGE"); + statusCodes.put(414, "REQUEST_URI_TOO_LONG"); + statusCodes.put(415, "UNSUPPORTED_MEDIA_TYPE"); + statusCodes.put(416, "REQUESTED_RANGE_NOT_SATISFIABLE"); + statusCodes.put(417, "EXPECTATION_FAILED"); + statusCodes.put(418, "I_AM_A_TEAPOT"); + statusCodes.put(419, "INSUFFICIENT_SPACE_ON_RESOURCE"); + statusCodes.put(420, "METHOD_FAILURE"); + statusCodes.put(421, "DESTINATION_LOCKED"); + statusCodes.put(422, "UNPROCESSABLE_ENTITY"); + statusCodes.put(423, "LOCKED"); + statusCodes.put(424, "FAILED_DEPENDENCY"); + statusCodes.put(426, "UPGRADE_REQUIRED"); + statusCodes.put(428, "PRECONDITION_REQUIRED"); + statusCodes.put(429, "TOO_MANY_REQUESTS"); + statusCodes.put(431, "REQUEST_HEADER_FIELDS_TOO_LARGE"); + + statusCodes.put(500, "INTERNAL_SERVER_ERROR"); + statusCodes.put(501, "NOT_IMPLEMENTED"); + statusCodes.put(502, "BAD_GATEWAY"); + statusCodes.put(503, "SERVICE_UNAVAILABLE"); + statusCodes.put(504, "GATEWAY_TIMEOUT"); + statusCodes.put(505, "HTTP_VERSION_NOT_SUPPORTED"); + statusCodes.put(506, "VARIANT_ALSO_NEGOTIATES"); + statusCodes.put(507, "INSUFFICIENT_STORAGE"); + statusCodes.put(508, "LOOP_DETECTED"); + statusCodes.put(509, "BANDWIDTH_LIMIT_EXCEEDED"); + statusCodes.put(510, "NOT_EXTENDED"); + statusCodes.put(511, "NETWORK_AUTHENTICATION_REQUIRED"); } + @Test + public void fromMapToEnum() { + for (Map.Entry entry : statusCodes.entrySet()) { + int value = entry.getKey(); + HttpStatus status = HttpStatus.valueOf(value); + assertEquals("Invalid value", value, status.value()); + assertEquals("Invalid name for [" + value + "]", entry.getValue(), status.name()); + } + } + + @Test + public void fromEnumToMap() { + + for (HttpStatus status : HttpStatus.values()) { + int value = status.value(); + if (value == 302) { + continue; + } + assertTrue("Map has no value for [" + value + "]", statusCodes.containsKey(value)); + assertEquals("Invalid name for [" + value + "]", statusCodes.get(value), status.name()); + } + } } From 8980ce712da209aa5cf742dd4bd58f09e5d46870 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 23 Jan 2012 14:26:11 +0100 Subject: [PATCH 20/20] SPR-8986 RestTemplate throws IllegalArgumentException when HTTP status is not in the HttpStatus enum - Added getRawStatusCode --- .../client/AbstractClientHttpResponse.java | 35 +++++++++++++++++++ .../BufferingClientHttpResponseWrapper.java | 6 +++- .../http/client/ClientHttpResponse.java | 9 ++++- .../client/CommonsClientHttpResponse.java | 9 +++-- .../HttpComponentsClientHttpResponse.java | 11 +++--- .../http/client/SimpleClientHttpResponse.java | 9 +++-- ...rceptingClientHttpRequestFactoryTests.java | 8 +++-- 7 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 org.springframework.web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java new file mode 100644 index 00000000000..cd6166575b4 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/http/client/AbstractClientHttpResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2012 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.http.client; + +import java.io.IOException; + +import org.springframework.http.HttpStatus; + +/** + * Abstract base for {@link ClientHttpResponse}. + * + * @author Arjen Poutsma + * @since 3.1.1 + */ +public abstract class AbstractClientHttpResponse implements ClientHttpResponse { + + public HttpStatus getStatusCode() throws IOException { + return HttpStatus.valueOf(getRawStatusCode()); + } + +} diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java b/org.springframework.web/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java index da9f39a52d5..f280790fec8 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java +++ b/org.springframework.web/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -47,6 +47,10 @@ final class BufferingClientHttpResponseWrapper implements ClientHttpResponse { return this.response.getStatusCode(); } + public int getRawStatusCode() throws IOException { + return this.response.getRawStatusCode(); + } + public String getStatusText() throws IOException { return this.response.getStatusText(); } diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/ClientHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/client/ClientHttpResponse.java index aba6fb06995..38aea117c76 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/client/ClientHttpResponse.java +++ b/org.springframework.web/src/main/java/org/springframework/http/client/ClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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,6 +39,13 @@ public interface ClientHttpResponse extends HttpInputMessage { */ HttpStatus getStatusCode() throws IOException; + /** + * Return the HTTP status code of the response as integer + * @return the HTTP status as an integer + * @throws IOException in case of I/O errors + */ + int getRawStatusCode() throws IOException; + /** * Return the HTTP status text of the response. * @return the HTTP status text diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/CommonsClientHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/client/CommonsClientHttpResponse.java index d3b93faee2d..7cc8cec6e93 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/client/CommonsClientHttpResponse.java +++ b/org.springframework.web/src/main/java/org/springframework/http/client/CommonsClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -23,7 +23,6 @@ import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpMethod; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; /** * {@link org.springframework.http.client.ClientHttpResponse} implementation that uses @@ -37,7 +36,7 @@ import org.springframework.http.HttpStatus; * @deprecated In favor of {@link HttpComponentsClientHttpResponse} */ @Deprecated -final class CommonsClientHttpResponse implements ClientHttpResponse { +final class CommonsClientHttpResponse extends AbstractClientHttpResponse { private final HttpMethod httpMethod; @@ -49,8 +48,8 @@ final class CommonsClientHttpResponse implements ClientHttpResponse { } - public HttpStatus getStatusCode() { - return HttpStatus.valueOf(this.httpMethod.getStatusCode()); + public int getRawStatusCode() { + return this.httpMethod.getStatusCode(); } public String getStatusText() { diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpResponse.java index 4b5e9a23bb0..d4033e4216a 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpResponse.java +++ b/org.springframework.web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -25,7 +25,6 @@ import org.apache.http.HttpResponse; import org.apache.http.util.EntityUtils; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; /** * {@link org.springframework.http.client.ClientHttpResponse} implementation that uses @@ -38,20 +37,20 @@ import org.springframework.http.HttpStatus; * @since 3.1 * @see HttpComponentsClientHttpRequest#execute() */ -final class HttpComponentsClientHttpResponse implements ClientHttpResponse { +final class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse { private final HttpResponse httpResponse; private HttpHeaders headers; - public HttpComponentsClientHttpResponse(HttpResponse httpResponse) { + HttpComponentsClientHttpResponse(HttpResponse httpResponse) { this.httpResponse = httpResponse; } - public HttpStatus getStatusCode() throws IOException { - return HttpStatus.valueOf(this.httpResponse.getStatusLine().getStatusCode()); + public int getRawStatusCode() throws IOException { + return this.httpResponse.getStatusLine().getStatusCode(); } public String getStatusText() throws IOException { diff --git a/org.springframework.web/src/main/java/org/springframework/http/client/SimpleClientHttpResponse.java b/org.springframework.web/src/main/java/org/springframework/http/client/SimpleClientHttpResponse.java index 5a8e3f04b34..1fc33f0b696 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/client/SimpleClientHttpResponse.java +++ b/org.springframework.web/src/main/java/org/springframework/http/client/SimpleClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -21,7 +21,6 @@ import java.io.InputStream; import java.net.HttpURLConnection; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; /** @@ -32,7 +31,7 @@ import org.springframework.util.StringUtils; * @author Arjen Poutsma * @since 3.0 */ -final class SimpleClientHttpResponse implements ClientHttpResponse { +final class SimpleClientHttpResponse extends AbstractClientHttpResponse { private final HttpURLConnection connection; @@ -44,8 +43,8 @@ final class SimpleClientHttpResponse implements ClientHttpResponse { } - public HttpStatus getStatusCode() throws IOException { - return HttpStatus.valueOf(this.connection.getResponseCode()); + public int getRawStatusCode() throws IOException { + return this.connection.getResponseCode(); } public String getStatusText() throws IOException { diff --git a/org.springframework.web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java b/org.springframework.web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java index b663a955706..70f5e257d25 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java +++ b/org.springframework.web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -291,6 +291,10 @@ public class InterceptingClientHttpRequestFactoryTests { return statusCode; } + public int getRawStatusCode() throws IOException { + return statusCode.value(); + } + public String getStatusText() throws IOException { return statusText; } @@ -300,7 +304,7 @@ public class InterceptingClientHttpRequestFactoryTests { } public InputStream getBody() throws IOException { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } public void close() {