Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ public class RedisIndexedSessionRepository

private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();

private BiFunction<String, Map<String, Object>, MapSession> redisSessionMapper = new RedisSessionMapper();
private BiFunction<String, Map<String, Object>, @Nullable MapSession> redisSessionMapper = new RedisSessionMapper();

/**
* Creates a new instance. For an example, refer to the class level javadoc.
Expand Down Expand Up @@ -757,11 +757,15 @@ public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {

/**
* Set the {@link BiFunction} used to map {@link MapSession} to a
* {@link ReactiveRedisSessionRepository.RedisSession}.
* {@link ReactiveRedisSessionRepository.RedisSession}. The mapper may return
* {@code null} to indicate that the session could not be loaded (for example when the
* Redis hash is incomplete); callers treat a {@code null} result the same as a
* missing session.
* @param redisSessionMapper the mapper to use, cannot be null
* @since 3.2
*/
public void setRedisSessionMapper(BiFunction<String, Map<String, Object>, MapSession> redisSessionMapper) {
public void setRedisSessionMapper(
BiFunction<String, Map<String, Object>, @Nullable MapSession> redisSessionMapper) {
Assert.notNull(redisSessionMapper, "redisSessionMapper cannot be null");
this.redisSessionMapper = redisSessionMapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,24 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import org.jspecify.annotations.Nullable;

import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.util.Assert;

/**
* A {@link Function} that converts a {@link Map} representing Redis hash to a
* {@link MapSession}.
* <p>
* This implementation never returns {@code null}; custom mappers used via
* {@code setRedisSessionMapper} may return {@code null} to signal an unloadable session.
*
* @author Vedran Pavic
* @author Marcus da Coregio
* @since 2.2.0
*/
public final class RedisSessionMapper implements BiFunction<String, Map<String, Object>, MapSession> {
public final class RedisSessionMapper implements BiFunction<String, Map<String, Object>, @Nullable MapSession> {

/**
* The key in the hash representing {@link Session#getCreationTime()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class RedisSessionRepository implements SessionRepository<RedisSessionRep

private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();

private BiFunction<String, Map<String, Object>, MapSession> redisSessionMapper = new RedisSessionMapper();
private BiFunction<String, Map<String, Object>, @Nullable MapSession> redisSessionMapper = new RedisSessionMapper();

/**
* Create a new {@link RedisSessionRepository} instance.
Expand Down Expand Up @@ -183,11 +183,15 @@ public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {

/**
* Set the {@link BiFunction} used to map {@link MapSession} to a
* {@link ReactiveRedisSessionRepository.RedisSession}.
* {@link ReactiveRedisSessionRepository.RedisSession}. The mapper may return
* {@code null} to indicate that the session could not be loaded (for example when the
* Redis hash is incomplete); callers treat a {@code null} result the same as a
* missing session.
* @param redisSessionMapper the mapper to use, cannot be null
* @since 3.2
*/
public void setRedisSessionMapper(BiFunction<String, Map<String, Object>, MapSession> redisSessionMapper) {
public void setRedisSessionMapper(
BiFunction<String, Map<String, Object>, @Nullable MapSession> redisSessionMapper) {
Assert.notNull(redisSessionMapper, "redisSessionMapper cannot be null");
this.redisSessionMapper = redisSessionMapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,20 @@ void findByIdWhenChangeSessionIdThenUsesSessionIdGenerator() {
assertThat(newSessionId).isEqualTo("test");
}

@Test
void findByIdWhenRedisSessionMapperReturnsNullThenReturnsNull() {
String sessionId = "session-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(sessionId)))
.willReturn(this.boundHashOperations);
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
Instant.now().toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
this.redisRepository.setRedisSessionMapper((id, entries) -> null);

assertThat(this.redisRepository.findById(sessionId)).isNull();
}

private String getKey(String id) {
return "spring:session:sessions:" + id;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,17 @@ void findByIdWhenChangeSessionIdThenUsesSessionIdGenerator() {
assertThat(session.changeSessionId()).isEqualTo("test");
}

@Test
void findByIdWhenRedisSessionMapperReturnsNullThenReturnsNull() {
given(this.sessionHashOperations.entries(eq(TEST_SESSION_KEY)))
.willReturn(mapOf(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
RedisSessionMapper.LAST_ACCESSED_TIME_KEY, Instant.now().toEpochMilli(),
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
this.sessionRepository.setRedisSessionMapper((id, entries) -> null);

assertThat(this.sessionRepository.findById(TEST_SESSION_ID)).isNull();
}

private static String getSessionKey(String sessionId) {
return "spring:session:sessions:" + sessionId;
}
Expand Down