SPR-6464 Polish FlashMap changes.

master
Rossen Stoyanchev 13 years ago
parent f0ed37c233
commit 671744aa62
  1. 3
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
  2. 60
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java
  3. 53
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMapManager.java
  4. 3
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/SmartView.java
  5. 6
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectAttributesMethodArgumentResolver.java
  6. 78
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributes.java
  7. 68
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMap.java
  8. 90
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/DefaultFlashMapManager.java
  9. 13
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContextUtils.java

@ -671,7 +671,8 @@ 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.
* <p>If no implementation is configured then we default to
* {@code org.springframework.web.servlet.support.DefaultFlashMapManager}.
*/
private void initFlashMapManager(ApplicationContext context) {
try {

@ -21,7 +21,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
/**
* A FlashMap provides a way for one request to store attributes intended for
@ -33,11 +32,14 @@ import org.springframework.util.Assert;
* <p>A FlashMap can be set up with a request path and request parameters to
* help identify the target request. Without this information, a FlashMap is
* made available to the next request, which may or may not be the intended
* result. Before a redirect, the target URL is known and when using the
* {@code org.springframework.web.servlet.view.RedirectView}, FlashMap
* instances are automatically updated with redirect URL information.
* recipient. On a redirect, the target URL is known and for example
* {@code org.springframework.web.servlet.view.RedirectView} has the
* opportunity to automatically update the current FlashMap with target
* URL information .
*
* <p>Annotated controllers will usually not access a FlashMap directly.. TODO
* <p>Annotated controllers will usually not use this type directly.
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
* for an overview of using flash attributes in annotated controllers.
*
* @author Rossen Stoyanchev
* @since 3.1
@ -58,13 +60,6 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
private final int createdBy;
/**
* Create a new instance.
*/
public FlashMap() {
this.createdBy = 0;
}
/**
* Create a new instance with an id uniquely identifying the creator of
* this FlashMap.
@ -72,20 +67,26 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
public FlashMap(int createdBy) {
this.createdBy = createdBy;
}
/**
* Create a new instance.
*/
public FlashMap() {
this.createdBy = 0;
}
/**
* Provide a URL path to help identify the target request for this FlashMap.
* The path may be absolute (e.g. /application/resource) or relative to the
* current request (e.g. ../resource).
* @param path the URI path, never {@code null}
* @param path the URI path
*/
public void setTargetRequestPath(String path) {
Assert.notNull(path, "Expected path must not be null");
this.targetRequestPath = path;
}
/**
* Return the URL path of the target request, or {@code null} if none.
* Return the target URL path or {@code null}.
*/
public String getTargetRequestPath() {
return targetRequestPath;
@ -93,10 +94,9 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
/**
* Provide request parameter pairs to identify the request for this FlashMap.
* If not set, the FlashMap will match to requests with any parameters.
* Only simple value types, as defined in {@link BeanUtils#isSimpleValueType},
* are used.
* Only simple type, non-null parameter values are used.
* @param params a Map with the names and values of expected parameters.
* @see BeanUtils#isSimpleValueType(Class)
*/
public FlashMap addTargetRequestParams(Map<String, ?> params) {
if (params != null) {
@ -112,10 +112,8 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
/**
* Provide a request parameter to identify the request for this FlashMap.
* If not set, the FlashMap will match to requests with any parameters.
*
* @param name the name of the expected parameter (never {@code null})
* @param value the value for the expected parameter (never {@code null})
* @param name the name of the expected parameter, never {@code null}
* @param value the value for the expected parameter, never {@code null}
*/
public FlashMap addTargetRequestParam(String name, String value) {
this.targetRequestParams.put(name, value.toString());
@ -130,9 +128,8 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
}
/**
* 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
* Start the expiration period for this instance.
* @param timeToLive the number of seconds before expiration
*/
public void startExpirationPeriod(int timeToLive) {
this.expirationStartTime = System.currentTimeMillis();
@ -140,8 +137,8 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
}
/**
* Whether the flash map has expired depending on the number of seconds
* elapsed since the call to {@link #startExpirationPeriod}.
* Whether this instance has expired depending on the amount of elapsed
* time since the call to {@link #startExpirationPeriod}.
*/
public boolean isExpired() {
if (this.expirationStartTime != 0) {
@ -160,8 +157,9 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
}
/**
* Compare two FlashMaps and select the one that has a target URL path or
* has more target request parameters.
* Compare two FlashMaps and prefer the one that specifies a target URL
* path or has more target URL parameters. Before comparing FlashMap
* instances ensure that they match a given request.
*/
public int compareTo(FlashMap other) {
int thisUrlPath = (this.targetRequestPath != null) ? 1 : 0;
@ -178,8 +176,8 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
public String toString() {
StringBuilder result = new StringBuilder();
result.append("[Attributes=").append(super.toString());
result.append(", expecteRequestUri=").append(this.targetRequestPath);
result.append(", expectedRequestParameters=" + this.targetRequestParams.toString()).append("]");
result.append(", targetRequestPath=").append(this.targetRequestPath);
result.append(", targetRequestParams=" + this.targetRequestParams.toString()).append("]");
return result.toString();
}

@ -16,20 +16,22 @@
package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
/**
* A strategy interface for storing and retrieving {@code FlashMap} instances.
* See {@link FlashMap} for a general overview of using flash attributes.
* A strategy interface for storing, retrieving, and managing {@code FlashMap}
* instances. See {@link FlashMap} for a general overview of flash attributes.
*
* <p>A FlashMapManager is invoked at the beginning and at the end of requests.
* For each request it retrieves an "input" FlashMap with attributes passed
* from a previous request (if any) and creates an "output" FlashMap with
* attributes to pass to a subsequent request. "Input" and "output" FlashMap
* instances are exposed as request attributes and are accessible via methods
* in {@code org.springframework.web.servlet.support.RequestContextUtils}.
*
* <p>A FlashMapManager is invoked at the beginning and at the end of a request.
* For each request, it exposes an "input" FlashMap with attributes passed from
* a previous request (if any) and an "output" FlashMap with attributes to pass
* to a subsequent request. Both FlashMap instances are exposed via request
* attributes and can be accessed through methods in
* {@code org.springframework.web.servlet.support.RequestContextUtils}.
* <p>Annotated controllers are most likely to store and access flash attributes
* through their model.
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}.
*
* @author Rossen Stoyanchev
* @since 3.1
@ -39,42 +41,39 @@ import javax.servlet.http.HttpServletRequest;
public interface FlashMapManager {
/**
* Name of request attribute that holds a read-only {@link Map} with
* "input" flash attributes from a previous request (if any).
* Name of request attribute that holds a read-only
* {@code Map<String, Object>} with "input" flash attributes if any.
* @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
*/
public static final String INPUT_FLASH_MAP_ATTRIBUTE = FlashMapManager.class.getName() + ".INPUT_FLASH_MAP";
/**
* Name of request attribute that holds the "output" {@link FlashMap} with
* attributes to pass to a subsequent request.
* attributes to save for a subsequent request.
* @see org.springframework.web.servlet.support.RequestContextUtils#getOutputFlashMap(HttpServletRequest)
*/
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = FlashMapManager.class.getName() + ".OUTPUT_FLASH_MAP";
/**
* Performs the following tasks unless the {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}
* Perform the following tasks unless the {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}
* request attribute exists:
* <ol>
* <li>Find the "input" FlashMap from a previous request (if any), expose it
* under the request attribute {@link #INPUT_FLASH_MAP_ATTRIBUTE}, and
* remove it from underlying storage.
* <li>Create the "output" FlashMap where the current request can save
* flash attributes and expose it under the request attribute
* {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}.
* <li>Remove expired FlashMap instances.
* <li>Find the "input" FlashMap, expose it under the request attribute
* {@link #INPUT_FLASH_MAP_ATTRIBUTE}, and remove it from underlying storage.
* <li>Create the "output" FlashMap and expose it under the request
* attribute {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}.
* <li>Clean expired FlashMap instances.
* </ol>
*
* @param request the current request
*/
void requestStarted(HttpServletRequest request);
/**
* Save the "output" FlashMap in underlying storage, start its expiration
* period, and decode/normalize its target request path.
*
* <p>The "output" FlashMap is not saved if it is empty or if it was not
* created by this FlashMapManager.
* Start the expiration period of the "output" FlashMap save it in the
* underlying storage.
* <p>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
*/
void requestCompleted(HttpServletRequest request);

@ -17,7 +17,8 @@
package org.springframework.web.servlet;
/**
* Interface to be implemented by Views that perform a redirect.
* Provides additional information about a View such as whether it
* performs redirects.
*
* @author Rossen Stoyanchev
* @since 3.1

@ -31,10 +31,10 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
/**
* Resolves {@link RedirectAttributesModelMap} method arguments.
* Resolves method arguments of type {@link RedirectAttributes}.
*
* <p>This resolver must be listed ahead of the {@link ModelMethodProcessor},
* which also resolves arguments of type {@link Map} and {@link Model}.
* <p>This resolver must be listed before the {@link ModelMethodProcessor},
* which resolves {@link Map} and {@link Model} arguments.
*
* @author Rossen Stoyanchev
* @since 3.1

@ -20,70 +20,66 @@ import java.util.Collection;
import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.web.servlet.FlashMap;
/**
* A {@link Model} that can also store attributes candidate for flash storage.
* A specialization of the {@link Model} interface that controllers can use to
* select attributes for a redirect scenario. Since the intent of adding
* redirect attributes is very explicit -- i.e. to be used for a redirect URL,
* attribute values may be formatted as Strings and stored that way to make
* them eligible to be appended to the query string or expanded as URI
* variables in {@code org.springframework.web.servlet.view.RedirectView}.
*
* <p>This interface also provides a way to add flash attributes. For a
* general overview of flash attributes see {@link FlashMap}. You can use
* {@link RedirectAttributes} to store flash attributes and they will be
* automatically propagated to the "output" FlashMap of the current request.
*
* <p>Example usage in an {@code @Controller}:
* <pre>
* &#064;RequestMapping(value = "/accounts", method = RequestMethod.POST)
* public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
* if (result.hasErrors()) {
* return "accounts/new";
* }
* // Save account ...
* redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
* return "redirect:/accounts/{id}";
* }
* </pre>
*
* <p>After the redirect, flash attributes are automatically added to the model
* of the controller serving the redirect URL.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public interface RedirectAttributes extends Model {
/**
* Add the supplied attribute under the supplied name.
* @param attributeName the name of the model attribute (never <code>null</code>)
* @param attributeValue the model attribute value (can be <code>null</code>)
*/
RedirectAttributes addAttribute(String attributeName, Object attributeValue);
/**
* Add the supplied attribute to this <code>Map</code> using a
* {@link org.springframework.core.Conventions#getVariableName generated name}.
* <p><emphasis>Note: Empty {@link java.util.Collection Collections} are not added to
* the model when using this method because we cannot correctly determine
* the true convention name. View code should check for <code>null</code> rather
* than for empty collections as is already done by JSTL tags.</emphasis>
* @param attributeValue the model attribute value (never <code>null</code>)
*/
RedirectAttributes addAttribute(Object attributeValue);
/**
* Copy all attributes in the supplied <code>Collection</code> into this
* <code>Map</code>, using attribute name generation for each element.
* @see #addAttribute(Object)
*/
RedirectAttributes addAllAttributes(Collection<?> attributeValues);
/**
* Copy all attributes in the supplied <code>Map</code> into this <code>Map</code>.
* @see #addAttribute(String, Object)
*/
Model addAllAttributes(Map<String, ?> attributes);
/**
* Copy all attributes in the supplied <code>Map</code> into this <code>Map</code>,
* with existing objects of the same name taking precedence (i.e. not getting
* replaced).
*/
RedirectAttributes mergeAttributes(Map<String, ?> attributes);
RedirectAttributes mergeAttributes(Map<String, ?> attributes);
/**
* Add the given attribute as a candidate for flash storage.
* @param attributeName the flash attribute name; never null
* @param attributeValue the flash attribute value; may be null
* Add the given flash attribute.
* @param attributeName the attribute name; never {@code null}
* @param attributeValue the attribute value; may be {@code null}
*/
RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue);
/**
* Add the given attribute value as a candidate for flash storage using a
* Add the given flash storage using a
* {@link org.springframework.core.Conventions#getVariableName generated name}.
* @param attributeValue the flash attribute value; never null
* @param attributeValue the flash attribute value; never {@code null}
*/
RedirectAttributes addFlashAttribute(Object attributeValue);
/**
* Return the attributes candidate for flash storage.
* Return the attributes candidate for flash storage or an empty Map.
*/
Map<String, ?> getFlashAttributes();
}
}

@ -23,12 +23,10 @@ import org.springframework.ui.ModelMap;
import org.springframework.validation.DataBinder;
/**
* A {@link ModelMap} that implements the {@link RedirectAttributes} interface.
*
* <p>Attributes are formatted and stored as Strings so they can be used as URI
* variables or as query parameters in the redirect URL. Alternatively,
* attributes may also be added as flash attributes in order to request storing
* them until the next request without affecting the redirect URL.
* A {@link ModelMap} implementation of {@link RedirectAttributes} that formats
* values as Strings using a {@link DataBinder}. Also provides a place to store
* flash attributes so they can survive a redirect without the need to be
* embedded in the redirect URL.
*
* @author Rossen Stoyanchev
* @since 3.1
@ -41,33 +39,31 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
private final ModelMap flashAttributes = new ModelMap();
/**
* Default constructor without a DataBinder.
* Redirect attribute values will be formatted via {@link #toString()}.
* Class constructor.
* @param dataBinder used to format attribute values as Strings.
*/
public RedirectAttributesModelMap() {
this(null);
public RedirectAttributesModelMap(DataBinder dataBinder) {
this.dataBinder = dataBinder;
}
/**
* Constructor with a DataBinder to use to format redirect attribute values.
* @param dataBinder a DataBinder for converting attribute values to String.
* Default constructor without a DataBinder.
* Attribute values are converted to String via {@link #toString()}.
*/
public RedirectAttributesModelMap(DataBinder dataBinder) {
this.dataBinder = dataBinder;
public RedirectAttributesModelMap() {
this(null);
}
/**
* Return the attributes candidate for flash storage.
* Return the attributes candidate for flash storage or an empty Map.
*/
public Map<String, ?> getFlashAttributes() {
return flashAttributes;
return this.flashAttributes;
}
/**
* Format the attribute value as a String and add it. If the value is
* {@code null} it is not be added.
* @param attributeName the attribute name; never null
* @param attributeValue the attribute value; skipped if null
* {@inheritDoc}
* <p>Formats the attribute value as a String before adding it.
*/
public RedirectAttributesModelMap addAttribute(String attributeName, Object attributeValue) {
if (attributeValue != null) {
@ -81,10 +77,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
* Add an attribute using a
* {@link org.springframework.core.Conventions#getVariableName generated name}.
* Before being added the attribute value is formatted as a String.
* @param attributeValue the attribute value; never null
* {@inheritDoc}
* <p>Formats the attribute value as a String before adding it.
*/
public RedirectAttributesModelMap addAttribute(Object attributeValue) {
super.addAttribute(attributeValue);
@ -92,9 +86,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
* Copy all attributes in the supplied <code>Collection</code> into this
* Model using attribute name generation for each element.
* @see #addAttribute(Object)
* {@inheritDoc}
* <p>Each attribute value is formatted as a String before being added.
*/
public RedirectAttributesModelMap addAllAttributes(Collection<?> attributeValues) {
super.addAllAttributes(attributeValues);
@ -102,8 +95,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
* Copy all supplied attributes into this model.
* @see #addAttribute(String, Object)
* {@inheritDoc}
* <p>Each attribute value is formatted as a String before being added.
*/
public RedirectAttributesModelMap addAllAttributes(Map<String, ?> attributes) {
if (attributes != null) {
@ -115,9 +108,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
* Copy all supplied attributes into this model with with existing
* attributes of the same name taking precedence (i.e. not getting replaced).
* @see #addAttribute(String, Object)
* {@inheritDoc}
* <p>Each attribute value is formatted as a String before being merged.
*/
public RedirectAttributesModelMap mergeAttributes(Map<String, ?> attributes) {
if (attributes != null) {
@ -134,21 +126,11 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
return this;
}
/**
* Add the given attribute as a candidate for flash storage.
* @param attributeName the flash attribute name; never null
* @param attributeValue the flash attribute value; may be null
*/
public RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue) {
this.flashAttributes.addAttribute(attributeName, attributeValue);
return this;
}
/**
* Add the given attribute value as a candidate for flash storage using a
* {@link org.springframework.core.Conventions#getVariableName generated name}.
* @param attributeValue the flash attribute value; never null
*/
public RedirectAttributes addFlashAttribute(Object attributeValue) {
this.flashAttributes.addAttribute(attributeValue);
return this;

@ -19,7 +19,6 @@ package org.springframework.web.servlet.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.http.HttpServletRequest;
@ -34,7 +33,8 @@ import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.util.UrlPathHelper;
/**
* A {@link FlashMapManager} that stores FlashMap instances in the HTTP session.
* A default {@link FlashMapManager} implementation keeps {@link FlashMap}
* instances in the HTTP session.
*
* @author Rossen Stoyanchev
* @since 3.1
@ -50,8 +50,9 @@ public class DefaultFlashMapManager implements FlashMapManager {
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
/**
* The amount of time in seconds after a FlashMap is saved (after request
* completion) before it is considered expired. The default value is 180.
* Set the amount of time in seconds after a {@link FlashMap} is saved
* (at request completion) and before it expires.
* <p>The default value is 180 seconds.
*/
public void setFlashMapTimeout(int flashTimeout) {
this.flashTimeout = flashTimeout;
@ -59,16 +60,16 @@ public class DefaultFlashMapManager implements FlashMapManager {
/**
* {@inheritDoc}
* <p>This method never causes the HTTP session to be created.
* <p>An HTTP session is never created by this method.
*/
public void requestStarted(HttpServletRequest request) {
if (request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE) != null) {
return;
}
Map<String, ?> inputFlashMap = lookupFlashMap(request);
FlashMap inputFlashMap = lookupFlashMap(request);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, inputFlashMap);
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
FlashMap outputFlashMap = new FlashMap(this.hashCode());
@ -78,17 +79,17 @@ public class DefaultFlashMapManager implements FlashMapManager {
}
/**
* Look up the "input" FlashMap by matching the target request path and
* the target request parameters configured in each available FlashMap
* to the current request.
*/
private Map<String, ?> lookupFlashMap(HttpServletRequest request) {
* Find the "input" FlashMap for the current request target by matching it
* to the target request information of all stored FlashMap instances.
* @return a FlashMap instance or {@code null}
*/
private FlashMap lookupFlashMap(HttpServletRequest request) {
List<FlashMap> allFlashMaps = retrieveFlashMaps(request, false);
if (CollectionUtils.isEmpty(allFlashMaps)) {
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Retrieved flash maps: " + allFlashMaps);
logger.debug("Retrieved FlashMap(s): " + allFlashMaps);
}
List<FlashMap> result = new ArrayList<FlashMap>();
for (FlashMap flashMap : allFlashMaps) {
@ -99,19 +100,19 @@ public class DefaultFlashMapManager implements FlashMapManager {
if (!result.isEmpty()) {
Collections.sort(result);
if (logger.isDebugEnabled()) {
logger.debug("Matching flash maps: " + result);
logger.debug("Found matching FlashMap(s): " + result);
}
FlashMap match = result.remove(0);
allFlashMaps.remove(match);
return Collections.unmodifiableMap(match);
return match;
}
return null;
}
/**
* Compares the target request path and the target request parameters in the
* given FlashMap and returns "true" if they match. If the FlashMap does not
* have target request information, it matches any request.
* Whether the given FlashMap matches the current request.
* The default implementation uses the target request path and query params
* saved in the FlashMap.
*/
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
if (flashMap.getTargetRequestPath() != null) {
@ -122,8 +123,8 @@ public class DefaultFlashMapManager implements FlashMapManager {
}
}
if (flashMap.getTargetRequestParams() != null) {
for (Map.Entry<String, String> entry : flashMap.getTargetRequestParams().entrySet()) {
if (!entry.getValue().equals(request.getParameter(entry.getKey()))) {
for (String paramName : flashMap.getTargetRequestParams().keySet()) {
if (!flashMap.getTargetRequestParams().get(paramName).equals(request.getParameter(paramName))) {
return false;
}
}
@ -132,10 +133,13 @@ public class DefaultFlashMapManager implements FlashMapManager {
}
/**
* Retrieve all available FlashMap instances from the HTTP session.
* Retrieve all FlashMap instances from the current HTTP session.
* If {@code allowCreate} is "true" and no flash maps exist yet, a new list
* is created and stored as a session attribute.
* @param request the current request
* @param allowCreate whether to create and save the FlashMap in the session
* @return a Map with all FlashMap instances; or {@code null}
* @param allowCreate whether to create the session if necessary
* @return a List to add FlashMap instances to or {@code null}
* assuming {@code allowCreate} is "false".
*/
@SuppressWarnings("unchecked")
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request, boolean allowCreate) {
@ -157,7 +161,7 @@ public class DefaultFlashMapManager implements FlashMapManager {
}
/**
* Iterate available FlashMap instances and remove the ones that have expired.
* Iterate all flash maps and remove expired ones.
*/
private void removeExpiredFlashMaps(HttpServletRequest request) {
List<FlashMap> allMaps = retrieveFlashMaps(request, false);
@ -173,40 +177,52 @@ public class DefaultFlashMapManager implements FlashMapManager {
expiredMaps.add(flashMap);
}
}
allMaps.removeAll(expiredMaps);
if (!expiredMaps.isEmpty()) {
allMaps.removeAll(expiredMaps);
}
}
/**
* {@inheritDoc}
* <p>An HTTP session is never created if the "output" FlashMap is empty.
*/
public void requestCompleted(HttpServletRequest request) {
FlashMap flashMap = (FlashMap) request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE);
if (flashMap == null) {
throw new IllegalStateException(
"Did not find a FlashMap exposed as the request attribute " + OUTPUT_FLASH_MAP_ATTRIBUTE);
throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created");
}
if (!flashMap.isEmpty() && flashMap.isCreatedBy(this.hashCode())) {
if (logger.isDebugEnabled()) {
logger.debug("Saving FlashMap=" + flashMap);
}
decodeAndNormalizeTargetPath(flashMap, request);
flashMap.startExpirationPeriod(this.flashTimeout);
onSaveFlashMap(flashMap, request);
retrieveFlashMaps(request, true).add(flashMap);
}
}
/**
* Ensure the target request path in the given FlashMap is decoded and also
* normalized (if it is relative) against the current request URL.
* Update a FlashMap before it is stored in the HTTP Session.
* <p>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
*/
private void decodeAndNormalizeTargetPath(FlashMap flashMap, HttpServletRequest request) {
String path = flashMap.getTargetRequestPath();
protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request) {
String targetPath = flashMap.getTargetRequestPath();
flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request));
flashMap.startExpirationPeriod(this.flashTimeout);
}
private String decodeAndNormalizePath(String path, HttpServletRequest request) {
if (path != null) {
path = urlPathHelper.decodeRequestString(request, path);
path = this.urlPathHelper.decodeRequestString(request, path);
if (path.charAt(0) != '/') {
String requestUri = this.urlPathHelper.getRequestUri(request);
path = requestUri.substring(0, requestUri.lastIndexOf('/') + 1) + path;
path = StringUtils.cleanPath(path);
}
flashMap.setTargetRequestPath(path);
}
return path;
}
}

@ -157,9 +157,11 @@ public abstract class RequestContextUtils {
}
/**
* Return a read-only Map with flash attributes saved during the previous request.
* Return a read-only {@link Map} with "input" flash attributes saved on a
* previous request.
* @param request the current request
* @return a read-only Map, or {@code null}
* @return a read-only Map, or {@code null}
* @see FlashMap
*/
@SuppressWarnings("unchecked")
public static Map<String, ?> getInputFlashMap(HttpServletRequest request) {
@ -167,9 +169,10 @@ public abstract class RequestContextUtils {
}
/**
* Return a FlashMap to add attributes to during the current request.
* @param request current HTTP request
* @return the flash map for the current request; never {@code null}.
* Return the "output" FlashMap with attributes to save for a subsequent request.
* @param request current request
* @return a {@link FlashMap} instance, never {@code null}
* @see FlashMap
*/
public static FlashMap getOutputFlashMap(HttpServletRequest request) {
return (FlashMap) request.getAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE);

Loading…
Cancel
Save