001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet; 018 019import java.util.HashMap; 020 021import org.springframework.util.LinkedMultiValueMap; 022import org.springframework.util.MultiValueMap; 023import org.springframework.util.ObjectUtils; 024import org.springframework.util.StringUtils; 025 026/** 027 * A FlashMap provides a way for one request to store attributes intended for 028 * use in another. This is most commonly needed when redirecting from one URL 029 * to another -- e.g. the Post/Redirect/Get pattern. A FlashMap is saved before 030 * the redirect (typically in the session) and is made available after the 031 * redirect and removed immediately. 032 * 033 * <p>A FlashMap can be set up with a request path and request parameters to 034 * help identify the target request. Without this information, a FlashMap is 035 * made available to the next request, which may or may not be the intended 036 * recipient. On a redirect, the target URL is known and a FlashMap can be 037 * updated with that information. This is done automatically when the 038 * {@code org.springframework.web.servlet.view.RedirectView} is used. 039 * 040 * <p>Note: annotated controllers will usually not use FlashMap directly. 041 * See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes} 042 * for an overview of using flash attributes in annotated controllers. 043 * 044 * @author Rossen Stoyanchev 045 * @since 3.1 046 * @see FlashMapManager 047 */ 048@SuppressWarnings("serial") 049public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> { 050 051 private String targetRequestPath; 052 053 private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<String, String>(4); 054 055 private long expirationTime = -1; 056 057 058 /** 059 * Provide a URL path to help identify the target request for this FlashMap. 060 * <p>The path may be absolute (e.g. "/application/resource") or relative to the 061 * current request (e.g. "../resource"). 062 */ 063 public void setTargetRequestPath(String path) { 064 this.targetRequestPath = path; 065 } 066 067 /** 068 * Return the target URL path (or {@code null} if none specified). 069 */ 070 public String getTargetRequestPath() { 071 return this.targetRequestPath; 072 } 073 074 /** 075 * Provide request parameters identifying the request for this FlashMap. 076 * @param params a Map with the names and values of expected parameters 077 */ 078 public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) { 079 if (params != null) { 080 for (String key : params.keySet()) { 081 for (String value : params.get(key)) { 082 addTargetRequestParam(key, value); 083 } 084 } 085 } 086 return this; 087 } 088 089 /** 090 * Provide a request parameter identifying the request for this FlashMap. 091 * @param name the expected parameter name (skipped if empty or {@code null}) 092 * @param value the expected value (skipped if empty or {@code null}) 093 */ 094 public FlashMap addTargetRequestParam(String name, String value) { 095 if (StringUtils.hasText(name) && StringUtils.hasText(value)) { 096 this.targetRequestParams.add(name, value); 097 } 098 return this; 099 } 100 101 /** 102 * Return the parameters identifying the target request, or an empty map. 103 */ 104 public MultiValueMap<String, String> getTargetRequestParams() { 105 return this.targetRequestParams; 106 } 107 108 /** 109 * Start the expiration period for this instance. 110 * @param timeToLive the number of seconds before expiration 111 */ 112 public void startExpirationPeriod(int timeToLive) { 113 this.expirationTime = System.currentTimeMillis() + timeToLive * 1000; 114 } 115 116 /** 117 * Set the expiration time for the FlashMap. This is provided for serialization 118 * purposes but can also be used instead {@link #startExpirationPeriod(int)}. 119 * @since 4.2 120 */ 121 public void setExpirationTime(long expirationTime) { 122 this.expirationTime = expirationTime; 123 } 124 125 /** 126 * Return the expiration time for the FlashMap or -1 if the expiration 127 * period has not started. 128 * @since 4.2 129 */ 130 public long getExpirationTime() { 131 return this.expirationTime; 132 } 133 134 /** 135 * Return whether this instance has expired depending on the amount of 136 * elapsed time since the call to {@link #startExpirationPeriod}. 137 */ 138 public boolean isExpired() { 139 return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime); 140 } 141 142 143 /** 144 * Compare two FlashMaps and prefer the one that specifies a target URL 145 * path or has more target URL parameters. Before comparing FlashMap 146 * instances ensure that they match a given request. 147 */ 148 @Override 149 public int compareTo(FlashMap other) { 150 int thisUrlPath = (this.targetRequestPath != null ? 1 : 0); 151 int otherUrlPath = (other.targetRequestPath != null ? 1 : 0); 152 if (thisUrlPath != otherUrlPath) { 153 return otherUrlPath - thisUrlPath; 154 } 155 else { 156 return other.targetRequestParams.size() - this.targetRequestParams.size(); 157 } 158 } 159 160 @Override 161 public boolean equals(Object other) { 162 if (this == other) { 163 return true; 164 } 165 if (!(other instanceof FlashMap)) { 166 return false; 167 } 168 FlashMap otherFlashMap = (FlashMap) other; 169 return (super.equals(otherFlashMap) && 170 ObjectUtils.nullSafeEquals(this.targetRequestPath, otherFlashMap.targetRequestPath) && 171 this.targetRequestParams.equals(otherFlashMap.targetRequestParams)); 172 } 173 174 @Override 175 public int hashCode() { 176 int result = super.hashCode(); 177 result = 31 * result + ObjectUtils.nullSafeHashCode(this.targetRequestPath); 178 result = 31 * result + this.targetRequestParams.hashCode(); 179 return result; 180 } 181 182 @Override 183 public String toString() { 184 return "FlashMap [attributes=" + super.toString() + ", targetRequestPath=" + 185 this.targetRequestPath + ", targetRequestParams=" + this.targetRequestParams + "]"; 186 } 187 188}