SPR-6464 Drop @FlashAttributes, add ResponseContext, ViewResponse, and RedirectResponse types for annotated controllers to use to prepare a redirect response with flash attributes; Add FlashMap and FlashMapManager and update DispatcherServlet to discover and invoke the FlashMapManager.
This commit is contained in:
@@ -29,6 +29,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -52,6 +53,7 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
import org.springframework.web.util.NestedServletException;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
@@ -178,6 +180,11 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
*/
|
||||
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
|
||||
|
||||
/**
|
||||
* Well-known name for the FlashMapManager object in the bean factory for this namespace.
|
||||
*/
|
||||
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current web application context.
|
||||
* Otherwise only the global web app context is obtainable by tags etc.
|
||||
@@ -202,7 +209,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
|
||||
*/
|
||||
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
|
||||
|
||||
|
||||
/** Log category to use when no mapped handler is found for a request. */
|
||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||
|
||||
@@ -269,6 +276,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
/** RequestToViewNameTranslator used by this servlet */
|
||||
private RequestToViewNameTranslator viewNameTranslator;
|
||||
|
||||
/** FlashMapManager used by this servlet */
|
||||
private FlashMapManager flashMapManager;
|
||||
|
||||
/** List of ViewResolvers used by this servlet */
|
||||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
@@ -414,6 +424,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
initHandlerExceptionResolvers(context);
|
||||
initRequestToViewNameTranslator(context);
|
||||
initViewResolvers(context);
|
||||
initFlashMapManager(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,6 +670,28 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the {@link FlashMapManager} used by this servlet instance.
|
||||
* <p>If no implementation is configured then we default to DefaultFlashMapManager.
|
||||
*/
|
||||
private void initFlashMapManager(ApplicationContext context) {
|
||||
try {
|
||||
this.flashMapManager =
|
||||
context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using FlashMapManager [" + this.flashMapManager + "]");
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// We need to use the default.
|
||||
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to locate FlashMapManager with name '" +
|
||||
FLASH_MAP_MANAGER_BEAN_NAME + "': using default [" + this.flashMapManager + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this servlet's ThemeSource, if any; else return <code>null</code>.
|
||||
* <p>Default is to return the WebApplicationContext as ThemeSource,
|
||||
@@ -782,6 +815,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
}
|
||||
}
|
||||
|
||||
boolean flashInitialized = this.flashMapManager.requestStarted(request);
|
||||
|
||||
// Make framework objects available to handlers and view objects.
|
||||
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
|
||||
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
|
||||
@@ -792,6 +827,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
doDispatch(request, response);
|
||||
}
|
||||
finally {
|
||||
if (flashInitialized) {
|
||||
this.flashMapManager.requestCompleted(request);
|
||||
}
|
||||
// Restore the original attribute snapshot, in case of an include.
|
||||
if (attributesSnapshot != null) {
|
||||
restoreAttributesAfterInclude(request, attributesSnapshot);
|
||||
|
||||
@@ -20,3 +20,5 @@ org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web
|
||||
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
|
||||
|
||||
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
|
||||
|
||||
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.DefaultFlashMapManager
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Stores attributes that need to be made available in the next request.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FlashMap extends ModelMap {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String keyParameterName;
|
||||
|
||||
private long expirationStartTime;
|
||||
|
||||
private int timeToLive;
|
||||
|
||||
/**
|
||||
* Create a FlashMap with a unique key.
|
||||
*/
|
||||
public FlashMap(String key, String keyParameterName) {
|
||||
Assert.notNull("The key is required", key);
|
||||
Assert.notNull("The key parameter name is required", keyParameterName);
|
||||
this.key = key;
|
||||
this.keyParameterName = keyParameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a FlashMap without a key.
|
||||
*/
|
||||
public FlashMap() {
|
||||
this.key = null;
|
||||
this.keyParameterName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key assigned to this FlashMap instance;
|
||||
* or {@code null} if a unique key has not been assigned.
|
||||
*/
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the request parameter to use when appending the flash
|
||||
* key to a redirect URL.
|
||||
*/
|
||||
public String getKeyParameterName() {
|
||||
return keyParameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the expiration period for this instance. After the given number of
|
||||
* seconds calls to {@link #isExpired()} will return "true".
|
||||
* @param timeToLive the number of seconds before flash map expires
|
||||
*/
|
||||
public void startExpirationPeriod(int timeToLive) {
|
||||
this.expirationStartTime = System.currentTimeMillis();
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the flash map has expired depending on the number of seconds
|
||||
* elapsed since the call to {@link #startExpirationPeriod}.
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
if (this.expirationStartTime != 0) {
|
||||
return (System.currentTimeMillis() - this.expirationStartTime) > this.timeToLive * 1000;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* A strategy interface for maintaining {@link FlashMap} instances in some
|
||||
* underlying storage between two requests. This is typically used when
|
||||
* redirecting from one URL to another.
|
||||
*
|
||||
* TODO ...
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see FlashMap
|
||||
*/
|
||||
public interface FlashMapManager {
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current request FlashMap.
|
||||
* @see RequestContextUtils#getFlashMap
|
||||
*/
|
||||
public static final String CURRENT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".CURRENT_FLASH_MAP";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the FlashMap from the previous request.
|
||||
*/
|
||||
public static final String PREVIOUS_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".PREVIOUS_FLASH_MAP";
|
||||
|
||||
/**
|
||||
* Perform flash storage tasks at the start of a new request:
|
||||
* <ul>
|
||||
* <li>Create a new FlashMap and make it available to the current request
|
||||
* under the request attribute {@link #CURRENT_FLASH_MAP_ATTRIBUTE}.
|
||||
* <li>Locate the FlashMap saved on the previous request and expose its
|
||||
* contents as attributes in the current request.
|
||||
* <li>Remove expired flash map instances.
|
||||
* </ul>
|
||||
*
|
||||
* @param request the current request
|
||||
*
|
||||
* @return "true" if flash storage tasks were performed; "false" otherwise
|
||||
* if the {@link #CURRENT_FLASH_MAP_ATTRIBUTE} request attribute exists.
|
||||
*/
|
||||
boolean requestStarted(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Access the current FlashMap through the {@link #CURRENT_FLASH_MAP_ATTRIBUTE}
|
||||
* request attribute and if not empty, save it in the underlying storage. This
|
||||
* method should be invoked after {@link #requestStarted} and if it returned "true".
|
||||
*/
|
||||
void requestCompleted(HttpServletRequest request);
|
||||
|
||||
}
|
||||
@@ -60,6 +60,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequ
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebArgumentResolverAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.support.ResponseContext;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHandlerMethodExceptionResolver} that supports using {@link ExceptionHandler}-annotated methods
|
||||
@@ -231,7 +232,9 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||
}
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex);
|
||||
ResponseContext responseContext = new ResponseContext(webRequest, mavContainer);
|
||||
|
||||
exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex, responseContext);
|
||||
|
||||
if (!mavContainer.isResolveView()) {
|
||||
return new ModelAndView();
|
||||
@@ -239,7 +242,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||
else {
|
||||
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
|
||||
mav.setViewName(mavContainer.getViewName());
|
||||
if (mavContainer.getView() != null) {
|
||||
if (!mavContainer.isViewReference()) {
|
||||
mav.setView((View) mavContainer.getView());
|
||||
}
|
||||
return mav;
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -47,13 +46,10 @@ import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.FlashStatus;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleFlashStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
@@ -61,7 +57,6 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.FlashAttributesHandler;
|
||||
import org.springframework.web.method.annotation.ModelFactory;
|
||||
import org.springframework.web.method.annotation.SessionAttributesHandler;
|
||||
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
|
||||
@@ -78,6 +73,8 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.FlashMapManager;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
@@ -94,6 +91,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletMode
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.support.ResponseContext;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -149,18 +147,16 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, SessionAttributesHandler>();
|
||||
|
||||
private final Map<Class<?>, FlashAttributesHandler> flashAttributesHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, FlashAttributesHandler>();
|
||||
|
||||
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||
|
||||
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
|
||||
|
||||
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
|
||||
|
||||
private final Map<Class<?>, Set<Method>> initBinderMethodCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
private final Map<Class<?>, WebDataBinderFactory> dataBinderFactoryCache =
|
||||
new ConcurrentHashMap<Class<?>, WebDataBinderFactory>();
|
||||
|
||||
private final Map<Class<?>, Set<Method>> modelAttributeMethodCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
private final Map<Class<?>, ModelFactory> modelFactoryCache = new ConcurrentHashMap<Class<?>, ModelFactory>();
|
||||
|
||||
/**
|
||||
* Create a {@link RequestMappingHandlerAdapter} instance.
|
||||
@@ -372,7 +368,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
argumentResolvers.addResolver(new HttpEntityMethodProcessor(messageConverters));
|
||||
argumentResolvers.addResolver(new ModelMethodProcessor());
|
||||
argumentResolvers.addResolver(new ErrorsMethodArgumentResolver());
|
||||
|
||||
|
||||
// Default-mode resolution
|
||||
argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(beanFactory, true));
|
||||
argumentResolvers.addResolver(new ServletModelAttributeMethodProcessor(true));
|
||||
@@ -462,8 +458,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
protected final ModelAndView handleInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception {
|
||||
|
||||
if (hasSessionAttributes(handlerMethod.getBeanType())) {
|
||||
|
||||
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
|
||||
// Always prevent caching in case of session attribute management.
|
||||
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
|
||||
}
|
||||
@@ -487,118 +483,106 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given handler type defines any handler-specific session attributes
|
||||
* via {@link SessionAttributes}.
|
||||
* Return the {@link SessionAttributesHandler} instance for the given
|
||||
* handler type, never {@code null}.
|
||||
*/
|
||||
private boolean hasSessionAttributes(Class<?> handlerType) {
|
||||
SessionAttributesHandler sessionAttrsHandler = null;
|
||||
synchronized(this.sessionAttributesHandlerCache) {
|
||||
sessionAttrsHandler = this.sessionAttributesHandlerCache.get(handlerType);
|
||||
if (sessionAttrsHandler == null) {
|
||||
sessionAttrsHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||
this.sessionAttributesHandlerCache.put(handlerType, sessionAttrsHandler);
|
||||
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
|
||||
if (sessionAttrHandler == null) {
|
||||
synchronized(this.sessionAttributesHandlerCache) {
|
||||
sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
|
||||
if (sessionAttrHandler == null) {
|
||||
sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||
this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
FlashAttributesHandler flashAttrsHandler = null;
|
||||
synchronized(this.flashAttributesHandlerCache) {
|
||||
flashAttrsHandler = this.flashAttributesHandlerCache.get(handlerType);
|
||||
if (flashAttrsHandler == null) {
|
||||
flashAttrsHandler = new FlashAttributesHandler(handlerType);
|
||||
this.flashAttributesHandlerCache.put(handlerType, flashAttrsHandler);
|
||||
}
|
||||
}
|
||||
return sessionAttrsHandler.hasSessionAttributes() || flashAttrsHandler.hasFlashAttributes();
|
||||
return sessionAttrHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required.
|
||||
*/
|
||||
private ModelAndView invokeHandlerMethod(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception {
|
||||
private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception {
|
||||
|
||||
WebDataBinderFactory binderFactory = createDataBinderFactory(handlerMethod);
|
||||
ModelFactory modelFactory = createModelFactory(handlerMethod, binderFactory);
|
||||
ServletInvocableHandlerMethod requestMethod = createRequestMappingMethod(handlerMethod, binderFactory);
|
||||
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
FlashStatus flashStatus = new SimpleFlashStatus();
|
||||
|
||||
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod);
|
||||
ModelFactory modelFactory = getModelFactory(handlerMethod);
|
||||
|
||||
FlashMap flashMap = (FlashMap) request.getAttribute(FlashMapManager.PREVIOUS_FLASH_MAP_ATTRIBUTE);
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, requestMethod);
|
||||
mavContainer.addAllAttributes(flashMap);
|
||||
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
|
||||
|
||||
requestMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus, flashStatus);
|
||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus, flashStatus);
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
ResponseContext responseContext = new ResponseContext(webRequest, mavContainer);
|
||||
|
||||
requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus, responseContext);
|
||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
|
||||
|
||||
if (!mavContainer.isResolveView()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
|
||||
mav.setViewName(mavContainer.getViewName());
|
||||
if (mavContainer.getView() != null) {
|
||||
if (!mavContainer.isViewReference()) {
|
||||
mav.setView((View) mavContainer.getView());
|
||||
}
|
||||
return mav;
|
||||
}
|
||||
}
|
||||
|
||||
private WebDataBinderFactory createDataBinderFactory(HandlerMethod handlerMethod) {
|
||||
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
|
||||
private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod) {
|
||||
ServletInvocableHandlerMethod requestMappingMethod =
|
||||
new ServletInvocableHandlerMethod(handlerMethod.getBean(), handlerMethod.getMethod());
|
||||
requestMappingMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
requestMappingMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
|
||||
requestMappingMethod.setDataBinderFactory(getDataBinderFactory(handlerMethod));
|
||||
requestMappingMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
return requestMappingMethod;
|
||||
}
|
||||
|
||||
private ModelFactory getModelFactory(HandlerMethod handlerMethod) {
|
||||
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
|
||||
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> binderMethods = initBinderMethodCache.get(handlerType);
|
||||
if (binderMethods == null) {
|
||||
binderMethods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
initBinderMethodCache.put(handlerType, binderMethods);
|
||||
ModelFactory modelFactory = this.modelFactoryCache.get(handlerType);
|
||||
if (modelFactory == null) {
|
||||
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)) {
|
||||
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
attrMethod.setDataBinderFactory(binderFactory);
|
||||
attrMethods.add(attrMethod);
|
||||
}
|
||||
modelFactory = new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
|
||||
this.modelFactoryCache.put(handlerType, modelFactory);
|
||||
}
|
||||
|
||||
for (Method method : binderMethods) {
|
||||
Object bean = handlerMethod.getBean();
|
||||
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
|
||||
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
|
||||
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
|
||||
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
initBinderMethods.add(binderMethod);
|
||||
}
|
||||
|
||||
return new ServletRequestDataBinderFactory(initBinderMethods, this.webBindingInitializer);
|
||||
return modelFactory;
|
||||
}
|
||||
|
||||
private ModelFactory createModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
|
||||
List<InvocableHandlerMethod> modelAttrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
|
||||
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> attributeMethods = modelAttributeMethodCache.get(handlerType);
|
||||
if (attributeMethods == null) {
|
||||
attributeMethods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
|
||||
modelAttributeMethodCache.put(handlerType, attributeMethods);
|
||||
WebDataBinderFactory binderFactory = this.dataBinderFactoryCache.get(handlerType);
|
||||
if (binderFactory == null) {
|
||||
List<InvocableHandlerMethod> binderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS)) {
|
||||
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
|
||||
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
|
||||
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
binderMethods.add(binderMethod);
|
||||
}
|
||||
binderFactory = new ServletRequestDataBinderFactory(binderMethods, this.webBindingInitializer);
|
||||
this.dataBinderFactoryCache.put(handlerType, binderFactory);
|
||||
}
|
||||
|
||||
for (Method method : attributeMethods) {
|
||||
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
attrMethod.setDataBinderFactory(binderFactory);
|
||||
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
modelAttrMethods.add(attrMethod);
|
||||
}
|
||||
|
||||
SessionAttributesHandler sessionAttrsHandler = sessionAttributesHandlerCache.get(handlerType);
|
||||
FlashAttributesHandler flashAttrsHandler = flashAttributesHandlerCache.get(handlerType);
|
||||
|
||||
return new ModelFactory(modelAttrMethods, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||
}
|
||||
|
||||
private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod,
|
||||
WebDataBinderFactory binderFactory) {
|
||||
Method method = handlerMethod.getMethod();
|
||||
ServletInvocableHandlerMethod requestMethod = new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
|
||||
requestMethod.setDataBinderFactory(binderFactory);
|
||||
requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
return requestMethod;
|
||||
return binderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -84,9 +83,11 @@ public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValue
|
||||
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
|
||||
ModelAndView mav = resolver.resolveModelAndView(method, handlerType, returnValue, model, request);
|
||||
if (mav != ModelAndViewResolver.UNRESOLVED) {
|
||||
mavContainer.setView(mav.getView());
|
||||
mavContainer.setViewName(mav.getViewName());
|
||||
mavContainer.addAllAttributes(mav.getModel());
|
||||
mavContainer.setViewName(mav.getViewName());
|
||||
if (!mav.isReference()) {
|
||||
mavContainer.setView(mav.getView());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* Provides annotated controller methods with convenience methods for setting
|
||||
* up response with a view name that will result in a redirect.
|
||||
*
|
||||
* <p>An instance of this class is obtained via {@link ResponseContext#redirect}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RedirectResponse {
|
||||
|
||||
private final NativeWebRequest webRequest;
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
RedirectResponse(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) {
|
||||
this.webRequest = webRequest;
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a URI template variable to use to expand the URI template into a URL.
|
||||
* <p><strong>Note:</strong> URI template variables from the current
|
||||
* request are automatically used when expanding the redirect URI template.
|
||||
* They don't need to be added explicitly here.
|
||||
*/
|
||||
public RedirectResponse uriVariable(String name, Object value) {
|
||||
this.mavContainer.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a URI template variable to use to expand the URI template into a URL.
|
||||
* The name of the variable is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
* <p><strong>Note:</strong> URI template variables from the current
|
||||
* request are automatically used when expanding the redirect URI template.
|
||||
* They don't need to be added explicitly here.
|
||||
*/
|
||||
public RedirectResponse uriVariable(Object value) {
|
||||
this.mavContainer.addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query parameter to append to the redirect URL.
|
||||
*/
|
||||
public RedirectResponse queryParam(String name, Object value) {
|
||||
this.mavContainer.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query parameter to append to the redirect URL.
|
||||
* The name of the parameter is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
*/
|
||||
public RedirectResponse queryParam(Object value) {
|
||||
this.mavContainer.addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flash attribute to save and make available in the model of the
|
||||
* target controller method after the redirect.
|
||||
*/
|
||||
public RedirectResponse flashAttribute(String name, Object value) {
|
||||
getFlashMap().addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flash attribute to save and make available in the model of the
|
||||
* target controller method after the redirect.
|
||||
* The name of the attribute is selected using a
|
||||
* {@link org.springframework.core.Conventions#getVariableName generated name}.
|
||||
*/
|
||||
public RedirectResponse flashAttribute(Object value) {
|
||||
getFlashMap().addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private FlashMap getFlashMap() {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return RequestContextUtils.getFlashMap(servletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Provides annotated controllers with convenience methods for setting up view
|
||||
* resolution.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ResponseContext {
|
||||
|
||||
private final NativeWebRequest webRequest;
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
public ResponseContext(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) {
|
||||
this.webRequest = webRequest;
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up view resolution based on the given view name and the implicit model
|
||||
* of the current request.
|
||||
*/
|
||||
public ViewResponse view(String viewName) {
|
||||
this.mavContainer.setViewName(viewName);
|
||||
return new ViewResponse(this.mavContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up view resolution for a redirect. This method clears the implicit
|
||||
* model. Use convenience methods on the returned {@link RedirectResponse}
|
||||
* instance to add URI variables, query parameters, and flash attributes
|
||||
* as necessary.
|
||||
* @param redirectUri a URI template either relative to the current URL or
|
||||
* absolute; do not prefix with "redirect:"
|
||||
*/
|
||||
public RedirectResponse redirect(String redirectUri) {
|
||||
if (!redirectUri.startsWith("redirect:")) {
|
||||
redirectUri = "redirect:" + redirectUri;
|
||||
}
|
||||
this.mavContainer.getModel().clear();
|
||||
this.mavContainer.setViewName(redirectUri);
|
||||
return new RedirectResponse(this.webRequest, this.mavContainer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Provides annotated controller methods with convenience methods for setting
|
||||
* up a response with a view name that does not have the "redirect:" prefix .
|
||||
*
|
||||
* <p>An instance of this class is obtained via {@link ResponseContext#view}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ViewResponse {
|
||||
|
||||
private final ModelAndViewContainer mavContainer;
|
||||
|
||||
ViewResponse(ModelAndViewContainer mavContainer) {
|
||||
this.mavContainer = mavContainer;
|
||||
}
|
||||
|
||||
public ViewResponse attribute(String attributeName, Object attributeValue) {
|
||||
this.mavContainer.addAttribute(attributeName, attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewResponse attribute(Object attributeValue) {
|
||||
this.mavContainer.addAttribute(attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.support;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.FlashMapManager;
|
||||
|
||||
/**
|
||||
* A default implementation that saves and retrieves FlashMap instances to and
|
||||
* from the HTTP session.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultFlashMapManager implements FlashMapManager {
|
||||
|
||||
static final String FLASH_MAPS_SESSION_ATTRIBUTE = DefaultFlashMapManager.class + ".FLASH_MAPS";
|
||||
|
||||
private boolean useUniqueFlashKey = true;
|
||||
|
||||
private String flashKeyParameterName = "_flashKey";
|
||||
|
||||
private int flashTimeout = 180;
|
||||
|
||||
private static final Random random = new Random();
|
||||
|
||||
/**
|
||||
* Whether each FlashMap instance should be stored with a unique key.
|
||||
* The unique key needs to be passed as a parameter in the redirect URL
|
||||
* and then used to look up the FlashMap instance avoiding potential
|
||||
* issues with concurrent requests.
|
||||
* <p>The default setting is "true".
|
||||
* <p>When set to "false" only one FlashMap is maintained making it
|
||||
* possible for a second concurrent request (e.g. via Ajax) to "consume"
|
||||
* the FlashMap inadvertently.
|
||||
*/
|
||||
public void setUseUniqueFlashKey(boolean useUniqueFlashKey) {
|
||||
this.useUniqueFlashKey = useUniqueFlashKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the name of the request parameter to be appended to the
|
||||
* redirect URL when using a unique flash key.
|
||||
* <p>The default value is "_flashKey".
|
||||
*/
|
||||
public void setFlashKeyParameterName(String parameterName) {
|
||||
this.flashKeyParameterName = parameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of time in seconds after a request has completed processing
|
||||
* and before a FlashMap is considered expired.
|
||||
* The default value is 180.
|
||||
*/
|
||||
public void setFlashMapTimeout(int flashTimeout) {
|
||||
this.flashTimeout = flashTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This method never creates an HTTP session. The current FlashMap is
|
||||
* exposed as a request attribute only and is not saved in the session
|
||||
* until {@link #requestCompleted}.
|
||||
*/
|
||||
public boolean requestStarted(HttpServletRequest request) {
|
||||
if (request.getAttribute(CURRENT_FLASH_MAP_ATTRIBUTE) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FlashMap currentFlashMap =
|
||||
this.useUniqueFlashKey ?
|
||||
new FlashMap(createFlashKey(request), this.flashKeyParameterName) : new FlashMap();
|
||||
request.setAttribute(CURRENT_FLASH_MAP_ATTRIBUTE, currentFlashMap);
|
||||
|
||||
FlashMap previousFlashMap = lookupPreviousFlashMap(request);
|
||||
if (previousFlashMap != null) {
|
||||
for (String name : previousFlashMap.keySet()) {
|
||||
if (request.getAttribute(name) == null) {
|
||||
request.setAttribute(name, previousFlashMap.get(name));
|
||||
}
|
||||
}
|
||||
// For exposing flash attributes in other places (e.g. annotated controllers)
|
||||
request.setAttribute(PREVIOUS_FLASH_MAP_ATTRIBUTE, previousFlashMap);
|
||||
}
|
||||
|
||||
// Check and remove expired instances
|
||||
Map<String, FlashMap> allFlashMaps = retrieveAllFlashMaps(request, false);
|
||||
if (allFlashMaps != null && !allFlashMaps.isEmpty()) {
|
||||
Iterator<FlashMap> iterator = allFlashMaps.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (iterator.next().isExpired()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique flash key. The default implementation uses {@link Random}.
|
||||
* @return the unique key; never {@code null}.
|
||||
*/
|
||||
protected String createFlashKey(HttpServletRequest request) {
|
||||
return String.valueOf(random.nextInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FlashMap from the previous request, if available.
|
||||
* If {@link #useUniqueFlashKey} is "true", a flash key parameter is
|
||||
* expected to be in the request. Otherwise there can be only one
|
||||
* FlashMap instance to return.
|
||||
*
|
||||
* @return the FlashMap from the previous request; or {@code null} if none.
|
||||
*/
|
||||
private FlashMap lookupPreviousFlashMap(HttpServletRequest request) {
|
||||
Map<String, FlashMap> flashMaps = retrieveAllFlashMaps(request, false);
|
||||
if (flashMaps != null && !flashMaps.isEmpty()) {
|
||||
if (this.useUniqueFlashKey) {
|
||||
String key = request.getParameter(this.flashKeyParameterName);
|
||||
if (key != null) {
|
||||
return flashMaps.remove(key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String key = flashMaps.keySet().iterator().next();
|
||||
return flashMaps.remove(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all FlashMap instances from the HTTP session in a thread-safe way.
|
||||
* @param request the current request
|
||||
* @param allowCreate whether to create and the FlashMap container if not found
|
||||
* @return a Map with all stored FlashMap instances; or {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, FlashMap> retrieveAllFlashMaps(HttpServletRequest request, boolean allowCreate) {
|
||||
HttpSession session = request.getSession(allowCreate);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, FlashMap> result = (Map<String, FlashMap>) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE);
|
||||
if (result == null && allowCreate) {
|
||||
synchronized (DefaultFlashMapManager.class) {
|
||||
result = (Map<String, FlashMap>) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE);
|
||||
if (result == null) {
|
||||
result = new ConcurrentHashMap<String, FlashMap>(5);
|
||||
session.setAttribute(FLASH_MAPS_SESSION_ATTRIBUTE, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The HTTP session is not created if the current FlashMap instance is empty.
|
||||
*/
|
||||
public void requestCompleted(HttpServletRequest request) {
|
||||
FlashMap flashMap = (FlashMap) request.getAttribute(CURRENT_FLASH_MAP_ATTRIBUTE);
|
||||
if (flashMap == null) {
|
||||
throw new IllegalStateException(
|
||||
"Did not find current FlashMap exposed as request attribute " + CURRENT_FLASH_MAP_ATTRIBUTE);
|
||||
}
|
||||
if (!flashMap.isEmpty()) {
|
||||
Map<String, FlashMap> allFlashMaps = retrieveAllFlashMaps(request, true);
|
||||
flashMap.startExpirationPeriod(this.flashTimeout);
|
||||
String key = this.useUniqueFlashKey ? flashMap.getKey() : "flashMap";
|
||||
allFlashMaps.put(key, flashMap);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.servlet.support;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -26,6 +27,8 @@ import org.springframework.ui.context.ThemeSource;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.FlashMapManager;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.ThemeResolver;
|
||||
|
||||
@@ -152,4 +155,13 @@ public abstract class RequestContextUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the flash map to use for the current request.
|
||||
* @param request current HTTP request
|
||||
* @return the flash map for the current request; never {@code null}.
|
||||
*/
|
||||
public static FlashMap getFlashMap(HttpServletRequest request) {
|
||||
return (FlashMap) request.getAttribute(FlashMapManager.CURRENT_FLASH_MAP_ATTRIBUTE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,14 +30,19 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
@@ -76,6 +81,7 @@ import org.springframework.web.util.WebUtils;
|
||||
* @author Colin Sampaleanu
|
||||
* @author Sam Brannen
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @see #setContextRelative
|
||||
* @see #setHttp10Compatible
|
||||
* @see #setExposeModelAttributes
|
||||
@@ -262,6 +268,14 @@ public class RedirectView extends AbstractUrlBasedView {
|
||||
appendQueryProperties(targetUrl, model, enc);
|
||||
}
|
||||
|
||||
FlashMap flashMap = RequestContextUtils.getFlashMap(request);
|
||||
if (flashMap != null && !flashMap.isEmpty()) {
|
||||
if (flashMap.getKey() != null) {
|
||||
ModelMap queryParam = new ModelMap(flashMap.getKeyParameterName(), flashMap.getKey());
|
||||
appendQueryProperties(targetUrl, queryParam, enc);
|
||||
}
|
||||
}
|
||||
|
||||
return targetUrl.toString();
|
||||
}
|
||||
|
||||
@@ -323,7 +337,7 @@ public class RedirectView extends AbstractUrlBasedView {
|
||||
}
|
||||
|
||||
// If there aren't already some parameters, we need a "?".
|
||||
boolean first = (getUrl().indexOf('?') < 0);
|
||||
boolean first = (targetUrl.toString().indexOf('?') < 0);
|
||||
for (Map.Entry<String, Object> entry : queryProperties(model).entrySet()) {
|
||||
Object rawValue = entry.getValue();
|
||||
Iterator<Object> valueIter;
|
||||
|
||||
Reference in New Issue
Block a user