Add support for session save mode
This commit introduces SaveMode enum that individual SessionRepository implementations use to allow customization of the way they handle save operation in terms of tracking delta of changes. Resolves: #1466
This commit is contained in:
@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -59,6 +60,8 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
public ReactiveRedisOperationsSessionRepository(ReactiveRedisOperations<String, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
@@ -90,6 +93,15 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ReactiveRedisOperations} used for sessions.
|
||||
* @return the {@link ReactiveRedisOperations} used for sessions
|
||||
@@ -102,12 +114,11 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
@Override
|
||||
public Mono<RedisSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
RedisSession session = new RedisSession();
|
||||
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
session.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
return Mono.just(session);
|
||||
});
|
||||
}
|
||||
@@ -132,7 +143,7 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
.filter((map) -> !map.isEmpty())
|
||||
.map(new RedisSessionMapper(id))
|
||||
.filter((session) -> !session.isExpired())
|
||||
.map(RedisSession::new)
|
||||
.map((session) -> new RedisSession(session, false))
|
||||
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -168,27 +179,20 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be
|
||||
* persisted in the next save operation.
|
||||
*/
|
||||
RedisSession() {
|
||||
this(new MapSession());
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}.
|
||||
* @param mapSession the {@link MapSession} that represents the persisted session
|
||||
* that was retrieved. Cannot be null.
|
||||
*/
|
||||
RedisSession(MapSession mapSession) {
|
||||
Assert.notNull(mapSession, "mapSession cannot be null");
|
||||
this.cached = mapSession;
|
||||
this.originalSessionId = mapSession.getId();
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (ReactiveRedisOperationsSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getAttributeKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,7 +207,12 @@ public class ReactiveRedisOperationsSessionRepository
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null
|
||||
&& ReactiveRedisOperationsSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.PrincipalNameIndexResolver;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
@@ -296,6 +297,8 @@ public class RedisOperationsSessionRepository
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
|
||||
@@ -363,6 +366,15 @@ public class RedisOperationsSessionRepository
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database index to use. Defaults to {@link #DEFAULT_DATABASE}.
|
||||
* @param database the database index to use
|
||||
@@ -439,7 +451,7 @@ public class RedisOperationsSessionRepository
|
||||
if (!allowExpired && loaded.isExpired()) {
|
||||
return null;
|
||||
}
|
||||
RedisSession result = new RedisSession(loaded);
|
||||
RedisSession result = new RedisSession(loaded, false);
|
||||
result.originalLastAccessTime = loaded.getLastAccessedTime();
|
||||
return result;
|
||||
}
|
||||
@@ -483,9 +495,11 @@ public class RedisOperationsSessionRepository
|
||||
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
Duration maxInactiveInterval = Duration.ofSeconds((this.defaultMaxInactiveInterval != null)
|
||||
? this.defaultMaxInactiveInterval : MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
RedisSession session = new RedisSession(maxInactiveInterval);
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
session.flushImmediateIfNecessary();
|
||||
return session;
|
||||
}
|
||||
@@ -674,32 +688,22 @@ public class RedisOperationsSessionRepository
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be
|
||||
* persisted in the next save operation.
|
||||
* @param maxInactiveInterval the amount of time that the {@link Session} should
|
||||
* be kept alive between client requests.
|
||||
*/
|
||||
RedisSession(Duration maxInactiveInterval) {
|
||||
this(new MapSession());
|
||||
this.cached.setMaxInactiveInterval(maxInactiveInterval);
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}.
|
||||
* @param cached the {@link MapSession} that represents the persisted session that
|
||||
* was retrieved. Cannot be null.
|
||||
*/
|
||||
RedisSession(MapSession cached) {
|
||||
Assert.notNull(cached, "MapSession cannot be null");
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
Map<String, String> indexes = RedisOperationsSessionRepository.this.indexResolver.resolveIndexesFor(this);
|
||||
this.originalPrincipalName = indexes.get(PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (RedisOperationsSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getSessionAttrNameKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
@@ -709,7 +713,8 @@ public class RedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
this.putAndFlush(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -744,7 +749,8 @@ public class RedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
this.putAndFlush(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -754,7 +760,12 @@ public class RedisOperationsSessionRepository
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null
|
||||
&& RedisOperationsSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -765,13 +776,15 @@ public class RedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
this.putAndFlush(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.cached.removeAttribute(attributeName);
|
||||
this.putAndFlush(getSessionAttrNameKey(attributeName), null);
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), null);
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
@@ -780,11 +793,6 @@ public class RedisOperationsSessionRepository
|
||||
}
|
||||
}
|
||||
|
||||
private void putAndFlush(String a, Object v) {
|
||||
this.delta.put(a, v);
|
||||
this.flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
private void save() {
|
||||
saveChangeSessionId();
|
||||
saveDelta();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Set;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -52,6 +53,8 @@ public class SimpleRedisOperationsSessionRepository
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Create a new {@link SimpleRedisOperationsSessionRepository} instance.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing
|
||||
@@ -89,9 +92,20 @@ public class SimpleRedisOperationsSessionRepository
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
RedisSession session = new RedisSession(this.defaultMaxInactiveInterval);
|
||||
MapSession cached = new MapSession();
|
||||
cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
session.flushIfRequired();
|
||||
return session;
|
||||
}
|
||||
@@ -120,7 +134,7 @@ public class SimpleRedisOperationsSessionRepository
|
||||
deleteById(sessionId);
|
||||
return null;
|
||||
}
|
||||
return new RedisSession(session);
|
||||
return new RedisSession(session, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,6 +155,10 @@ public class SimpleRedisOperationsSessionRepository
|
||||
return this.keyNamespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
private static String getAttributeKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal {@link Session} implementation used by this {@link SessionRepository}.
|
||||
*/
|
||||
@@ -154,18 +172,20 @@ public class SimpleRedisOperationsSessionRepository
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
RedisSession(Duration maxInactiveInterval) {
|
||||
this(new MapSession());
|
||||
this.cached.setMaxInactiveInterval(maxInactiveInterval);
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
RedisSession(MapSession cached) {
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (SimpleRedisOperationsSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getAttributeKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,7 +200,12 @@ public class SimpleRedisOperationsSessionRepository
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null
|
||||
&& SimpleRedisOperationsSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,7 +216,8 @@ public class SimpleRedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
putAttribute(RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName, attributeValue);
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -207,7 +233,8 @@ public class SimpleRedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
putAttribute(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,7 +245,8 @@ public class SimpleRedisOperationsSessionRepository
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
putAttribute(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,11 +302,6 @@ public class SimpleRedisOperationsSessionRepository
|
||||
this.delta.clear();
|
||||
}
|
||||
|
||||
private void putAttribute(String name, Object value) {
|
||||
this.delta.put(name, value);
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
@@ -119,4 +120,12 @@ public @interface EnableRedisHttpSession {
|
||||
*/
|
||||
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
* @return the save mode
|
||||
* @since 2.2.0
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
@@ -86,6 +87,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
||||
|
||||
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
|
||||
@@ -117,6 +120,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setFlushMode(this.flushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
return sessionRepository;
|
||||
@@ -165,6 +169,10 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
public void setCleanupCron(String cleanupCron) {
|
||||
this.cleanupCron = cleanupCron;
|
||||
}
|
||||
@@ -240,6 +248,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
flushMode = redisFlushMode.getFlushMode();
|
||||
}
|
||||
this.flushMode = flushMode;
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
String cleanupCron = attributes.getString("cleanupCron");
|
||||
if (StringUtils.hasText(cleanupCron)) {
|
||||
this.cleanupCron = cleanupCron;
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
@@ -98,4 +99,12 @@ public @interface EnableRedisWebSession {
|
||||
@Deprecated
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
* @return the save mode
|
||||
* @since 2.2.0
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
@@ -61,6 +62,8 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private String redisNamespace = ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private ReactiveRedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
@@ -78,6 +81,7 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -94,6 +98,10 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setRedisConnectionFactory(
|
||||
@SpringSessionRedisConnectionFactory ObjectProvider<ReactiveRedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||
@@ -132,6 +140,7 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
}
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
}
|
||||
|
||||
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
|
||||
|
||||
@@ -32,6 +32,7 @@ import reactor.test.StepVerifier;
|
||||
import org.springframework.data.redis.core.ReactiveHashOperations;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -130,7 +131,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession newSession = this.repository.new RedisSession();
|
||||
RedisSession newSession = this.repository.new RedisSession(new MapSession(), true);
|
||||
StepVerifier.create(this.repository.save(newSession)).verifyComplete();
|
||||
|
||||
verify(this.redisOperations).opsForHash();
|
||||
@@ -154,7 +155,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession session = this.repository.new RedisSession(new MapSession(this.cached));
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -170,7 +171,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -193,7 +194,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -216,7 +217,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.repository.new RedisSession(new MapSession(), false);
|
||||
session.removeAttribute(attrName);
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -234,7 +235,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
@Test
|
||||
void redisSessionGetAttributes() {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
@@ -325,7 +326,7 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test // gh-1120
|
||||
void getAttributeNamesAndRemove() {
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -336,6 +337,78 @@ class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnSetAttribute() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(1);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnGetAttribute() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(2);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeAlways() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ALWAYS);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(3);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
private Map<String, Object> map(Object... objects) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (objects == null) {
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
@@ -146,7 +147,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached);
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(session.getLastAccessedTime());
|
||||
String originalId = session.getId();
|
||||
String changeSessionId = session.changeSessionId();
|
||||
@@ -194,7 +195,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
// gh-467
|
||||
@Test
|
||||
void saveSessionNothingChanged() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached);
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
|
||||
this.redisRepository.save(session);
|
||||
|
||||
@@ -230,7 +231,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveJavadoc() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached);
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(session.getLastAccessedTime());
|
||||
|
||||
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id"))
|
||||
@@ -252,7 +253,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveLastAccessChanged() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(this.cached));
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
@@ -267,7 +268,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
@Test
|
||||
void saveSetAttribute() {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
@@ -282,7 +283,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
@Test
|
||||
void saveRemoveAttribute() {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.removeAttribute(attrName);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
@@ -295,7 +296,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveExpired() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
@@ -310,8 +311,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
@Test
|
||||
void redisSessionGetAttributes() {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.redisRepository.new RedisSession(
|
||||
Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
RedisSession session = this.redisRepository.createSession();
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
assertThat(session.getAttributeNames()).containsOnly(attrName);
|
||||
@@ -746,7 +746,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
void changeRedisNamespace() {
|
||||
String namespace = "foo:bar";
|
||||
this.redisRepository.setRedisKeyNamespace(namespace);
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
@@ -772,7 +772,7 @@ class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test // gh-1120
|
||||
void getAttributeNamesAndRemove() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached);
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -836,6 +836,57 @@ class RedisOperationsSessionRepositoryTests {
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnSetAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.redisRepository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.redisRepository.save(session);
|
||||
assertThat(getDelta()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnGetAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.redisRepository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.redisRepository.save(session);
|
||||
assertThat(getDelta()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeAlways() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ALWAYS);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.redisRepository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.redisRepository.save(session);
|
||||
assertThat(getDelta()).hasSize(3);
|
||||
}
|
||||
|
||||
private String getKey(String id) {
|
||||
return "spring:session:sessions:" + id;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.data.redis.SimpleRedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@@ -125,6 +126,19 @@ class SimpleRedisOperationsSessionRepositoryTests {
|
||||
.withMessage("flushMode must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setSaveMode_ValidSaveMode_ShouldSetSaveMode() {
|
||||
this.sessionRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
assertThat(ReflectionTestUtils.getField(this.sessionRepository, "saveMode"))
|
||||
.isEqualTo(SaveMode.ON_GET_ATTRIBUTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setSaveMode_NullSaveMode_ShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sessionRepository.setSaveMode(null))
|
||||
.withMessage("saveMode must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createSession_DefaultMaxInactiveInterval_ShouldCreateSession() {
|
||||
RedisSession redisSession = this.sessionRepository.createSession();
|
||||
@@ -223,6 +237,69 @@ class SimpleRedisOperationsSessionRepositoryTests {
|
||||
verifyNoMoreInteractions(this.sessionHashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void save_WithSaveModeOnSetAttribute_SholdSaveSession() {
|
||||
given(this.sessionRedisOperations.hasKey(eq(TEST_SESSION_KEY))).willReturn(true);
|
||||
this.sessionRepository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute1", "value1");
|
||||
attributes.put("attribute2", "value2");
|
||||
attributes.put("attribute3", "value3");
|
||||
RedisSession session = createTestSession(attributes);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.sessionRepository.save(session);
|
||||
verify(this.sessionRedisOperations).hasKey(eq(TEST_SESSION_KEY));
|
||||
verify(this.sessionRedisOperations).opsForHash();
|
||||
verify(this.sessionRedisOperations).expireAt(eq(TEST_SESSION_KEY), eq(getExpiry(session)));
|
||||
verify(this.sessionHashOperations).putAll(eq(TEST_SESSION_KEY), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(1);
|
||||
verifyNoMoreInteractions(this.sessionRedisOperations);
|
||||
verifyNoMoreInteractions(this.sessionHashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnGetAttribute() {
|
||||
given(this.sessionRedisOperations.hasKey(eq(TEST_SESSION_KEY))).willReturn(true);
|
||||
this.sessionRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute1", "value1");
|
||||
attributes.put("attribute2", "value2");
|
||||
attributes.put("attribute3", "value3");
|
||||
RedisSession session = createTestSession(attributes);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.sessionRepository.save(session);
|
||||
verify(this.sessionRedisOperations).hasKey(eq(TEST_SESSION_KEY));
|
||||
verify(this.sessionRedisOperations).opsForHash();
|
||||
verify(this.sessionRedisOperations).expireAt(eq(TEST_SESSION_KEY), eq(getExpiry(session)));
|
||||
verify(this.sessionHashOperations).putAll(eq(TEST_SESSION_KEY), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(2);
|
||||
verifyNoMoreInteractions(this.sessionRedisOperations);
|
||||
verifyNoMoreInteractions(this.sessionHashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeAlways() {
|
||||
given(this.sessionRedisOperations.hasKey(eq(TEST_SESSION_KEY))).willReturn(true);
|
||||
this.sessionRepository.setSaveMode(SaveMode.ALWAYS);
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute1", "value1");
|
||||
attributes.put("attribute2", "value2");
|
||||
attributes.put("attribute3", "value3");
|
||||
RedisSession session = createTestSession(attributes);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
this.sessionRepository.save(session);
|
||||
verify(this.sessionRedisOperations).hasKey(eq(TEST_SESSION_KEY));
|
||||
verify(this.sessionRedisOperations).opsForHash();
|
||||
verify(this.sessionRedisOperations).expireAt(eq(TEST_SESSION_KEY), eq(getExpiry(session)));
|
||||
verify(this.sessionHashOperations).putAll(eq(TEST_SESSION_KEY), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(3);
|
||||
verifyNoMoreInteractions(this.sessionRedisOperations);
|
||||
verifyNoMoreInteractions(this.sessionHashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void save_SessionNotExists_ShouldThrowException() {
|
||||
RedisSession session = createTestSession();
|
||||
@@ -315,12 +392,16 @@ class SimpleRedisOperationsSessionRepositoryTests {
|
||||
return result;
|
||||
}
|
||||
|
||||
private RedisSession createTestSession() {
|
||||
private RedisSession createTestSession(Map<String, Object> attributes) {
|
||||
MapSession cached = new MapSession(TEST_SESSION_ID);
|
||||
cached.setCreationTime(Instant.EPOCH);
|
||||
cached.setLastAccessedTime(Instant.EPOCH);
|
||||
cached.setAttribute("attribute1", "value1");
|
||||
return this.sessionRepository.new RedisSession(cached);
|
||||
attributes.forEach(cached::setAttribute);
|
||||
return this.sessionRepository.new RedisSession(cached, false);
|
||||
}
|
||||
|
||||
private RedisSession createTestSession() {
|
||||
return createTestSession(Collections.singletonMap("attribute1", "value1"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
@@ -142,6 +143,20 @@ class RedisHttpSessionConfigurationTests {
|
||||
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSaveModeAnnotation() {
|
||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionAnnotationConfiguration.class);
|
||||
assertThat(this.context.getBean(RedisOperationsSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||
SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSaveModeSetter() {
|
||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionSetterConfiguration.class);
|
||||
assertThat(this.context.getBean(RedisOperationsSessionRepository.class)).hasFieldOrPropertyWithValue("saveMode",
|
||||
SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void qualifiedConnectionFactoryRedisConfig() {
|
||||
registerAndRefresh(RedisConfig.class, QualifiedConnectionFactoryRedisConfig.class);
|
||||
@@ -299,6 +314,20 @@ class RedisHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisHttpSession(saveMode = SaveMode.ALWAYS)
|
||||
static class CustomSaveModeExpressionAnnotationConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomSaveModeExpressionSetterConfiguration extends RedisHttpSessionConfiguration {
|
||||
|
||||
CustomSaveModeExpressionSetterConfiguration() {
|
||||
setSaveMode(SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
static class QualifiedConnectionFactoryRedisConfig {
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
@@ -107,6 +108,20 @@ class RedisWebSessionConfigurationTests {
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSaveModeAnnotation() {
|
||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionAnnotationConfiguration.class);
|
||||
assertThat(this.context.getBean(ReactiveRedisOperationsSessionRepository.class))
|
||||
.hasFieldOrPropertyWithValue("saveMode", SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customSaveModeSetter() {
|
||||
registerAndRefresh(RedisConfig.class, CustomSaveModeExpressionSetterConfiguration.class);
|
||||
assertThat(this.context.getBean(ReactiveRedisOperationsSessionRepository.class))
|
||||
.hasFieldOrPropertyWithValue("saveMode", SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void qualifiedConnectionFactoryRedisConfig() {
|
||||
registerAndRefresh(RedisConfig.class, QualifiedConnectionFactoryRedisConfig.class);
|
||||
@@ -249,6 +264,20 @@ class RedisWebSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession(saveMode = SaveMode.ALWAYS)
|
||||
static class CustomSaveModeExpressionAnnotationConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomSaveModeExpressionSetterConfiguration extends RedisWebSessionConfiguration {
|
||||
|
||||
CustomSaveModeExpressionSetterConfiguration() {
|
||||
setSaveMode(SaveMode.ALWAYS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession
|
||||
static class QualifiedConnectionFactoryRedisConfig {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user