Tighten FlashMapManager for use with alternative storage options
Ensure that both FlashMapManager methods - the one invoked at the start of a request and the one invoked before a redirect, update the underlying storage fully since it's not guaranteed that both will be invoked on any given request. Also move the logic to remove expired FlashMap instances to the metohd invoked at the start of a request to ensure the check is made frequently enough. SPR-8997
This commit is contained in:
parent
598b125bb2
commit
a3bb3769ba
|
|
@ -840,14 +840,14 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||||
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
|
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
|
||||||
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
|
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
|
||||||
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
|
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
|
||||||
|
|
||||||
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
|
||||||
|
if (inputFlashMap != null) {
|
||||||
|
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
|
||||||
|
}
|
||||||
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
|
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
|
||||||
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
|
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
|
||||||
|
|
||||||
Map<String, ?> flashMap = this.flashMapManager.getFlashMapForRequest(request);
|
|
||||||
if (flashMap != null) {
|
|
||||||
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, flashMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doDispatch(request, response);
|
doDispatch(request, response);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -32,12 +32,11 @@ import org.springframework.util.StringUtils;
|
||||||
* <p>A FlashMap can be set up with a request path and request parameters to
|
* <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
|
* 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
|
* made available to the next request, which may or may not be the intended
|
||||||
* recipient. On a redirect, the target URL is known and for example
|
* recipient. On a redirect, the target URL is known and a FlashMap can be
|
||||||
* {@code org.springframework.web.servlet.view.RedirectView} has the
|
* updated with that information. This is done automatically when the
|
||||||
* opportunity to automatically update the current FlashMap with target
|
* {@code org.springframework.web.servlet.view.RedirectView} is used.
|
||||||
* URL information.
|
|
||||||
*
|
*
|
||||||
* <p>Annotated controllers will usually not use this type directly.
|
* <p>Note: annotated controllers will usually not use FlashMap directly.
|
||||||
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
|
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
|
||||||
* for an overview of using flash attributes in annotated controllers.
|
* for an overview of using flash attributes in annotated controllers.
|
||||||
*
|
*
|
||||||
|
|
@ -58,25 +57,6 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
|
||||||
|
|
||||||
private int timeToLive;
|
private int timeToLive;
|
||||||
|
|
||||||
private final int createdBy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance.
|
|
||||||
*/
|
|
||||||
public FlashMap() {
|
|
||||||
this.createdBy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a URL path to help identify the target request for this FlashMap.
|
* 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
|
* The path may be absolute (e.g. /application/resource) or relative to the
|
||||||
|
|
@ -96,7 +76,6 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide request parameters identifying the request for this FlashMap.
|
* Provide request parameters identifying the request for this FlashMap.
|
||||||
* Null or empty keys and values are skipped.
|
|
||||||
* @param params a Map with the names and values of expected parameters.
|
* @param params a Map with the names and values of expected parameters.
|
||||||
*/
|
*/
|
||||||
public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
|
public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
|
||||||
|
|
@ -112,8 +91,8 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a request parameter identifying the request for this FlashMap.
|
* Provide a request parameter identifying the request for this FlashMap.
|
||||||
* @param name the expected parameter name, skipped if {@code null}
|
* @param name the expected parameter name, skipped if empty or {@code null}
|
||||||
* @param value the expected parameter value, skipped if {@code null}
|
* @param value the expected value, skipped if empty or {@code null}
|
||||||
*/
|
*/
|
||||||
public FlashMap addTargetRequestParam(String name, String value) {
|
public FlashMap addTargetRequestParam(String name, String value) {
|
||||||
if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
|
if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
|
||||||
|
|
@ -151,13 +130,6 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the given id matches the id of the creator of this FlashMap.
|
|
||||||
*/
|
|
||||||
public boolean isCreatedBy(int createdBy) {
|
|
||||||
return this.createdBy == createdBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two FlashMaps and prefer the one that specifies a target URL
|
* Compare two FlashMaps and prefer the one that specifies a target URL
|
||||||
* path or has more target URL parameters. Before comparing FlashMap
|
* path or has more target URL parameters. Before comparing FlashMap
|
||||||
|
|
|
||||||
|
|
@ -16,44 +16,43 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet;
|
package org.springframework.web.servlet;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A strategy interface for retrieving and saving FlashMap instances.
|
* A strategy interface for retrieving and saving FlashMap instances.
|
||||||
* See {@link FlashMap} for a general overview of flash attributes.
|
* See {@link FlashMap} for a general overview of flash attributes.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*
|
*
|
||||||
* @see FlashMap
|
* @see FlashMap
|
||||||
*/
|
*/
|
||||||
public interface FlashMapManager {
|
public interface FlashMapManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a Map with flash attributes saved by a previous request.
|
* Find a FlashMap saved by a previous request that matches to the current
|
||||||
* See {@link FlashMap} for details on how FlashMap instances
|
* request, remove it from underlying storage, and also remove other
|
||||||
* identifies the target requests they're saved for.
|
* expired FlashMap instances.
|
||||||
* If found, the Map is removed from the underlying storage.
|
* <p>This method is invoked in the beginning of every request in contrast
|
||||||
|
* to {@link #saveOutputFlashMap}, which is invoked only when there are
|
||||||
|
* flash attributes to be saved - i.e. before a redirect.
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return a read-only Map with flash attributes or {@code null}
|
* @param response the current response
|
||||||
|
* @return a FlashMap matching the current request or {@code null}
|
||||||
*/
|
*/
|
||||||
Map<String, ?> getFlashMapForRequest(HttpServletRequest request);
|
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the given FlashMap, in some underlying storage, mark the beginning
|
* Save the given FlashMap, in some underlying storage and set the start
|
||||||
* of its expiration period, and remove other expired FlashMap instances.
|
* of its expiration period.
|
||||||
* The method has no impact if the FlashMap is empty and there are no
|
|
||||||
* expired FlashMap instances to be removed.
|
|
||||||
* <p><strong>Note:</strong> Invoke this method prior to a redirect in order
|
* <p><strong>Note:</strong> Invoke this method prior to a redirect in order
|
||||||
* to allow saving the FlashMap in the HTTP session or perhaps in a response
|
* to allow saving the FlashMap in the HTTP session or in a response
|
||||||
* cookie before the response is committed.
|
* cookie before the response is committed.
|
||||||
* @param flashMap the FlashMap to save
|
* @param flashMap the FlashMap to save
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param response the current response
|
* @param response the current response
|
||||||
*/
|
*/
|
||||||
void save(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
|
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package org.springframework.web.servlet.support;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
@ -50,6 +49,8 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
|
||||||
|
|
||||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||||
|
|
||||||
|
private static final Object writeLock = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the amount of time in seconds after a {@link FlashMap} is saved
|
* Set the amount of time in seconds after a {@link FlashMap} is saved
|
||||||
* (at request completion) and before it expires.
|
* (at request completion) and before it expires.
|
||||||
|
|
@ -81,34 +82,30 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
|
||||||
return this.urlPathHelper;
|
return this.urlPathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
|
||||||
* {@inheritDoc}
|
List<FlashMap> allMaps = retrieveFlashMaps(request);
|
||||||
* <p>Does not cause an HTTP session to be created.
|
if (CollectionUtils.isEmpty(allMaps)) {
|
||||||
*/
|
|
||||||
public final Map<String, ?> getFlashMapForRequest(HttpServletRequest request) {
|
|
||||||
List<FlashMap> flashMaps = retrieveFlashMaps(request);
|
|
||||||
if (CollectionUtils.isEmpty(flashMaps)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Retrieved FlashMap(s): " + flashMaps);
|
logger.debug("Retrieved FlashMap(s): " + allMaps);
|
||||||
}
|
}
|
||||||
List<FlashMap> result = new ArrayList<FlashMap>();
|
List<FlashMap> mapsToRemove = getExpiredFlashMaps(allMaps);
|
||||||
for (FlashMap flashMap : flashMaps) {
|
FlashMap match = getMatchingFlashMap(allMaps, request);
|
||||||
if (isFlashMapForRequest(flashMap, request)) {
|
if (match != null) {
|
||||||
result.add(flashMap);
|
mapsToRemove.add(match);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!result.isEmpty()) {
|
if (!mapsToRemove.isEmpty()) {
|
||||||
Collections.sort(result);
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Found matching FlashMap(s): " + result);
|
logger.debug("Removing FlashMap(s): " + allMaps);
|
||||||
|
}
|
||||||
|
synchronized (writeLock) {
|
||||||
|
allMaps = retrieveFlashMaps(request);
|
||||||
|
allMaps.removeAll(mapsToRemove);
|
||||||
|
updateFlashMaps(allMaps, request, response);
|
||||||
}
|
}
|
||||||
FlashMap match = result.remove(0);
|
|
||||||
flashMaps.remove(match);
|
|
||||||
return Collections.unmodifiableMap(match);
|
|
||||||
}
|
}
|
||||||
return null;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -118,10 +115,44 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
|
||||||
*/
|
*/
|
||||||
protected abstract List<FlashMap> retrieveFlashMaps(HttpServletRequest request);
|
protected abstract List<FlashMap> retrieveFlashMaps(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of expired FlashMap instances contained in the given list.
|
||||||
|
*/
|
||||||
|
private List<FlashMap> getExpiredFlashMaps(List<FlashMap> allMaps) {
|
||||||
|
List<FlashMap> result = new ArrayList<FlashMap>();
|
||||||
|
for (FlashMap map : allMaps) {
|
||||||
|
if (map.isExpired()) {
|
||||||
|
result.add(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a FlashMap contained in the given list that matches the request.
|
||||||
|
* @return a matching FlashMap or {@code null}
|
||||||
|
*/
|
||||||
|
private FlashMap getMatchingFlashMap(List<FlashMap> allMaps, HttpServletRequest request) {
|
||||||
|
List<FlashMap> result = new ArrayList<FlashMap>();
|
||||||
|
for (FlashMap flashMap : allMaps) {
|
||||||
|
if (isFlashMapForRequest(flashMap, request)) {
|
||||||
|
result.add(flashMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
Collections.sort(result);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Found matching FlashMap(s): " + result);
|
||||||
|
}
|
||||||
|
return result.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the given FlashMap matches the current request.
|
* Whether the given FlashMap matches the current request.
|
||||||
* The default implementation uses the target request path and query params
|
* The default implementation uses the target request path and query
|
||||||
* saved in the FlashMap.
|
* parameters saved in the FlashMap.
|
||||||
*/
|
*/
|
||||||
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
|
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
|
||||||
if (flashMap.getTargetRequestPath() != null) {
|
if (flashMap.getTargetRequestPath() != null) {
|
||||||
|
|
@ -142,37 +173,21 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
|
||||||
* {@inheritDoc}
|
if (CollectionUtils.isEmpty(flashMap)) {
|
||||||
* <p>The FlashMap, if not empty, is saved to the HTTP session.
|
|
||||||
*/
|
|
||||||
public final void save(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
Assert.notNull(flashMap, "FlashMap must not be null");
|
|
||||||
|
|
||||||
List<FlashMap> flashMaps = retrieveFlashMaps(request);
|
|
||||||
if (flashMap.isEmpty() && (flashMaps == null)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
|
||||||
boolean update = false;
|
flashMap.setTargetRequestPath(path);
|
||||||
flashMaps = retrieveFlashMaps(request);
|
flashMap.startExpirationPeriod(this.flashMapTimeout);
|
||||||
if (!CollectionUtils.isEmpty(flashMaps)) {
|
if (logger.isDebugEnabled()) {
|
||||||
update = removeExpired(flashMaps);
|
logger.debug("Saving FlashMap=" + flashMap);
|
||||||
}
|
}
|
||||||
if (!flashMap.isEmpty()) {
|
synchronized (writeLock) {
|
||||||
String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
|
List<FlashMap> allMaps = retrieveFlashMaps(request);
|
||||||
flashMap.setTargetRequestPath(path);
|
allMaps = (allMaps == null) ? new CopyOnWriteArrayList<FlashMap>() : allMaps;
|
||||||
flashMap.startExpirationPeriod(this.flashMapTimeout);
|
allMaps.add(flashMap);
|
||||||
if (logger.isDebugEnabled()) {
|
updateFlashMaps(allMaps, request, response);
|
||||||
logger.debug("Saving FlashMap=" + flashMap);
|
|
||||||
}
|
|
||||||
flashMaps = (flashMaps == null) ? new CopyOnWriteArrayList<FlashMap>() : flashMaps;
|
|
||||||
flashMaps.add(flashMap);
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
if (update) {
|
|
||||||
updateFlashMaps(flashMaps, request, response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,25 +212,4 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
|
||||||
protected abstract void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request,
|
protected abstract void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request,
|
||||||
HttpServletResponse response);
|
HttpServletResponse response);
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove expired FlashMap instances from the given List.
|
|
||||||
*/
|
|
||||||
protected boolean removeExpired(List<FlashMap> flashMaps) {
|
|
||||||
List<FlashMap> expired = new ArrayList<FlashMap>();
|
|
||||||
for (FlashMap flashMap : flashMaps) {
|
|
||||||
if (flashMap.isExpired()) {
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Removing expired FlashMap: " + flashMap);
|
|
||||||
}
|
|
||||||
expired.add(flashMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (expired.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return flashMaps.removeAll(expired);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import javax.servlet.http.HttpSession;
|
||||||
import org.springframework.web.servlet.FlashMap;
|
import org.springframework.web.servlet.FlashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores {@link FlashMap} instances in the HTTP session.
|
* Store and retrieve {@link FlashMap} instances to and from the HTTP session.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
|
|
@ -35,9 +35,10 @@ public class SessionFlashMapManager extends AbstractFlashMapManager{
|
||||||
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS";
|
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve saved FlashMap instances from the HTTP session.
|
* Retrieve saved FlashMap instances from the HTTP Session.
|
||||||
* @param request the current request
|
* <p>Does not cause an HTTP session to be created but may update it if a
|
||||||
* @return a List with FlashMap instances or {@code null}
|
* FlashMap matching the current request is found or there are expired
|
||||||
|
* FlashMap to be removed.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
|
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
|
||||||
|
|
@ -46,7 +47,7 @@ public class SessionFlashMapManager extends AbstractFlashMapManager{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the given FlashMap instances in the HTTP session.
|
* Save the given FlashMap instance, if not empty, in the HTTP session.
|
||||||
*/
|
*/
|
||||||
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
|
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
|
||||||
request.getSession().setAttribute(FLASH_MAPS_SESSION_ATTRIBUTE, flashMaps);
|
request.getSession().setAttribute(FLASH_MAPS_SESSION_ATTRIBUTE, flashMaps);
|
||||||
|
|
|
||||||
|
|
@ -276,7 +276,7 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView {
|
||||||
}
|
}
|
||||||
|
|
||||||
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
|
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
|
||||||
flashMapManager.save(flashMap, request, response);
|
flashMapManager.saveOutputFlashMap(flashMap, request, response);
|
||||||
|
|
||||||
sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
|
sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import static org.junit.Assert.assertTrue;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
@ -60,7 +59,7 @@ public class AbstractFlashMapManagerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestByPath() {
|
public void retrieveAndUpdateMatchByPath() {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
flashMap.setTargetRequestPath("/path");
|
flashMap.setTargetRequestPath("/path");
|
||||||
|
|
@ -68,16 +67,15 @@ public class AbstractFlashMapManagerTests {
|
||||||
this.flashMapManager.setFlashMaps(flashMap);
|
this.flashMapManager.setFlashMaps(flashMap);
|
||||||
|
|
||||||
this.request.setRequestURI("/path");
|
this.request.setRequestURI("/path");
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMap, inputFlashMap);
|
assertEquals(flashMap, inputFlashMap);
|
||||||
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-8779
|
// SPR-8779
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestByOriginatingPath() {
|
public void retrieveAndUpdateMatchByOriginatingPath() {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
flashMap.setTargetRequestPath("/accounts");
|
flashMap.setTargetRequestPath("/accounts");
|
||||||
|
|
@ -86,14 +84,14 @@ public class AbstractFlashMapManagerTests {
|
||||||
|
|
||||||
this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts");
|
this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts");
|
||||||
this.request.setRequestURI("/mvc/accounts");
|
this.request.setRequestURI("/mvc/accounts");
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMap, inputFlashMap);
|
assertEquals(flashMap, inputFlashMap);
|
||||||
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestByPathWithTrailingSlash() {
|
public void retrieveAndUpdateMatchWithTrailingSlash() {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
flashMap.setTargetRequestPath("/path");
|
flashMap.setTargetRequestPath("/path");
|
||||||
|
|
@ -101,14 +99,14 @@ public class AbstractFlashMapManagerTests {
|
||||||
this.flashMapManager.setFlashMaps(flashMap);
|
this.flashMapManager.setFlashMaps(flashMap);
|
||||||
|
|
||||||
this.request.setRequestURI("/path/");
|
this.request.setRequestURI("/path/");
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMap, inputFlashMap);
|
assertEquals(flashMap, inputFlashMap);
|
||||||
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestWithParams() {
|
public void retrieveAndUpdateMatchByParams() {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
flashMap.addTargetRequestParam("number", "one");
|
flashMap.addTargetRequestParam("number", "one");
|
||||||
|
|
@ -116,19 +114,19 @@ public class AbstractFlashMapManagerTests {
|
||||||
this.flashMapManager.setFlashMaps(flashMap);
|
this.flashMapManager.setFlashMaps(flashMap);
|
||||||
|
|
||||||
this.request.setParameter("number", (String) null);
|
this.request.setParameter("number", (String) null);
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertNull(inputFlashMap);
|
assertNull(inputFlashMap);
|
||||||
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
||||||
|
|
||||||
this.request.setParameter("number", "two");
|
this.request.setParameter("number", "two");
|
||||||
inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertNull(inputFlashMap);
|
assertNull(inputFlashMap);
|
||||||
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
||||||
|
|
||||||
this.request.setParameter("number", "one");
|
this.request.setParameter("number", "one");
|
||||||
inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMap, inputFlashMap);
|
assertEquals(flashMap, inputFlashMap);
|
||||||
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
||||||
|
|
@ -137,7 +135,7 @@ public class AbstractFlashMapManagerTests {
|
||||||
// SPR-8798
|
// SPR-8798
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestWithMultiValueParam() {
|
public void retrieveAndUpdateMatchWithMultiValueParam() {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("name", "value");
|
flashMap.put("name", "value");
|
||||||
flashMap.addTargetRequestParam("id", "1");
|
flashMap.addTargetRequestParam("id", "1");
|
||||||
|
|
@ -146,20 +144,20 @@ public class AbstractFlashMapManagerTests {
|
||||||
this.flashMapManager.setFlashMaps(flashMap);
|
this.flashMapManager.setFlashMaps(flashMap);
|
||||||
|
|
||||||
this.request.setParameter("id", "1");
|
this.request.setParameter("id", "1");
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertNull(inputFlashMap);
|
assertNull(inputFlashMap);
|
||||||
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
assertEquals("FlashMap should not have been removed", 1, this.flashMapManager.getFlashMaps().size());
|
||||||
|
|
||||||
this.request.addParameter("id", "2");
|
this.request.addParameter("id", "2");
|
||||||
inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMap, inputFlashMap);
|
assertEquals(flashMap, inputFlashMap);
|
||||||
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
assertEquals("Input FlashMap should have been removed", 0, this.flashMapManager.getFlashMaps().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFlashMapForRequestSortOrder() {
|
public void retrieveAndUpdateSortMultipleMatches() {
|
||||||
FlashMap emptyFlashMap = new FlashMap();
|
FlashMap emptyFlashMap = new FlashMap();
|
||||||
|
|
||||||
FlashMap flashMapOne = new FlashMap();
|
FlashMap flashMapOne = new FlashMap();
|
||||||
|
|
@ -174,28 +172,44 @@ public class AbstractFlashMapManagerTests {
|
||||||
this.flashMapManager.setFlashMaps(emptyFlashMap, flashMapOne, flashMapTwo);
|
this.flashMapManager.setFlashMaps(emptyFlashMap, flashMapOne, flashMapTwo);
|
||||||
|
|
||||||
this.request.setRequestURI("/one/two");
|
this.request.setRequestURI("/one/two");
|
||||||
Map<String, ?> inputFlashMap = this.flashMapManager.getFlashMapForRequest(this.request);
|
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
assertEquals(flashMapTwo, inputFlashMap);
|
assertEquals(flashMapTwo, inputFlashMap);
|
||||||
|
assertEquals("Input FlashMap should have been removed", 2, this.flashMapManager.getFlashMaps().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveFlashMapEmpty() throws InterruptedException {
|
public void retrieveAndUpdateRemoveExpired() throws InterruptedException {
|
||||||
|
List<FlashMap> flashMaps = new ArrayList<FlashMap>();
|
||||||
|
for (int i=0; i < 5; i++) {
|
||||||
|
FlashMap expiredFlashMap = new FlashMap();
|
||||||
|
expiredFlashMap.startExpirationPeriod(-1);
|
||||||
|
flashMaps.add(expiredFlashMap);
|
||||||
|
}
|
||||||
|
this.flashMapManager.setFlashMaps(flashMaps);
|
||||||
|
this.flashMapManager.retrieveAndUpdate(this.request, this.response);
|
||||||
|
|
||||||
|
assertEquals("Expired instances should be removed even if the saved FlashMap is empty",
|
||||||
|
0, this.flashMapManager.getFlashMaps().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveOutputFlashMapEmpty() throws InterruptedException {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
|
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
List<FlashMap> allMaps = this.flashMapManager.getFlashMaps();
|
List<FlashMap> allMaps = this.flashMapManager.getFlashMaps();
|
||||||
|
|
||||||
assertNull(allMaps);
|
assertNull(allMaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveFlashMap() throws InterruptedException {
|
public void saveOutputFlashMap() throws InterruptedException {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("name", "value");
|
flashMap.put("name", "value");
|
||||||
|
|
||||||
this.flashMapManager.setFlashMapTimeout(-1); // expire immediately so we can check expiration started
|
this.flashMapManager.setFlashMapTimeout(-1); // expire immediately so we can check expiration started
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
List<FlashMap> allMaps = this.flashMapManager.getFlashMaps();
|
List<FlashMap> allMaps = this.flashMapManager.getFlashMaps();
|
||||||
|
|
||||||
assertNotNull(allMaps);
|
assertNotNull(allMaps);
|
||||||
|
|
@ -204,67 +218,52 @@ public class AbstractFlashMapManagerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveFlashMapDecodeTargetPath() throws InterruptedException {
|
public void saveOutputFlashMapDecodeTargetPath() throws InterruptedException {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
|
|
||||||
flashMap.setTargetRequestPath("/once%20upon%20a%20time");
|
flashMap.setTargetRequestPath("/once%20upon%20a%20time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once upon a time", flashMap.getTargetRequestPath());
|
assertEquals("/once upon a time", flashMap.getTargetRequestPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveFlashMapNormalizeTargetPath() throws InterruptedException {
|
public void saveOutputFlashMapNormalizeTargetPath() throws InterruptedException {
|
||||||
FlashMap flashMap = new FlashMap();
|
FlashMap flashMap = new FlashMap();
|
||||||
flashMap.put("key", "value");
|
flashMap.put("key", "value");
|
||||||
|
|
||||||
flashMap.setTargetRequestPath(".");
|
flashMap.setTargetRequestPath(".");
|
||||||
this.request.setRequestURI("/once/upon/a/time");
|
this.request.setRequestURI("/once/upon/a/time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once/upon/a", flashMap.getTargetRequestPath());
|
assertEquals("/once/upon/a", flashMap.getTargetRequestPath());
|
||||||
|
|
||||||
flashMap.setTargetRequestPath("./");
|
flashMap.setTargetRequestPath("./");
|
||||||
this.request.setRequestURI("/once/upon/a/time");
|
this.request.setRequestURI("/once/upon/a/time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once/upon/a/", flashMap.getTargetRequestPath());
|
assertEquals("/once/upon/a/", flashMap.getTargetRequestPath());
|
||||||
|
|
||||||
flashMap.setTargetRequestPath("..");
|
flashMap.setTargetRequestPath("..");
|
||||||
this.request.setRequestURI("/once/upon/a/time");
|
this.request.setRequestURI("/once/upon/a/time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once/upon", flashMap.getTargetRequestPath());
|
assertEquals("/once/upon", flashMap.getTargetRequestPath());
|
||||||
|
|
||||||
flashMap.setTargetRequestPath("../");
|
flashMap.setTargetRequestPath("../");
|
||||||
this.request.setRequestURI("/once/upon/a/time");
|
this.request.setRequestURI("/once/upon/a/time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once/upon/", flashMap.getTargetRequestPath());
|
assertEquals("/once/upon/", flashMap.getTargetRequestPath());
|
||||||
|
|
||||||
flashMap.setTargetRequestPath("../../only");
|
flashMap.setTargetRequestPath("../../only");
|
||||||
this.request.setRequestURI("/once/upon/a/time");
|
this.request.setRequestURI("/once/upon/a/time");
|
||||||
this.flashMapManager.save(flashMap, this.request, this.response);
|
this.flashMapManager.saveOutputFlashMap(flashMap, this.request, this.response);
|
||||||
|
|
||||||
assertEquals("/once/only", flashMap.getTargetRequestPath());
|
assertEquals("/once/only", flashMap.getTargetRequestPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void saveFlashMapAndRemoveExpired() throws InterruptedException {
|
|
||||||
List<FlashMap> flashMaps = new ArrayList<FlashMap>();
|
|
||||||
for (int i=0; i < 5; i++) {
|
|
||||||
FlashMap flashMap = new FlashMap();
|
|
||||||
flashMap.startExpirationPeriod(-1);
|
|
||||||
flashMaps.add(flashMap);
|
|
||||||
}
|
|
||||||
this.flashMapManager.setFlashMaps(flashMaps);
|
|
||||||
this.flashMapManager.save(new FlashMap(), request, response);
|
|
||||||
|
|
||||||
assertEquals("Expired instances should be removed even if the saved FlashMap is empty",
|
|
||||||
0, this.flashMapManager.getFlashMaps().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class TestFlashMapManager extends AbstractFlashMapManager {
|
private static class TestFlashMapManager extends AbstractFlashMapManager {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue