diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepository.java index be375f50..0901e75a 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepository.java @@ -21,7 +21,6 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.function.Function; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @@ -47,29 +46,6 @@ public class ReactiveRedisOperationsSessionRepository implements */ public static final String DEFAULT_NAMESPACE = "spring:session"; - /** - * The key in the Hash representing {@link Session#getCreationTime()}. - */ - static final String CREATION_TIME_KEY = "creationTime"; - - /** - * The key in the Hash representing {@link Session#getLastAccessedTime()}. - */ - static final String LAST_ACCESSED_TIME_KEY = "lastAccessedTime"; - - /** - * The key in the Hash representing {@link Session#getMaxInactiveInterval()} . - */ - static final String MAX_INACTIVE_INTERVAL_KEY = "maxInactiveInterval"; - - /** - * The prefix of the key used for session attributes. The suffix is the name of - * the session attribute. For example, if the session contained an attribute named - * attributeName, then there would be an entry in the hash named - * sessionAttr:attributeName that mapped to its value. - */ - static final String ATTRIBUTE_PREFIX = "sessionAttr:"; - private final ReactiveRedisOperations sessionRedisOperations; /** @@ -162,7 +138,7 @@ public class ReactiveRedisOperationsSessionRepository implements return this.sessionRedisOperations.opsForHash().entries(sessionKey) .collectMap((e) -> e.getKey().toString(), Map.Entry::getValue) .filter((map) -> !map.isEmpty()) - .map(new SessionMapper(id)) + .map(new RedisSessionMapper(id)) .filter((session) -> !session.isExpired()) .map(RedisSession::new) .switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty()))); @@ -177,7 +153,7 @@ public class ReactiveRedisOperationsSessionRepository implements } private static String getAttributeKey(String attributeName) { - return ATTRIBUTE_PREFIX + attributeName; + return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName; } private String getSessionKey(String sessionId) { @@ -206,10 +182,12 @@ public class ReactiveRedisOperationsSessionRepository implements */ RedisSession() { this(new MapSession()); - this.delta.put(CREATION_TIME_KEY, getCreationTime().toEpochMilli()); - this.delta.put(MAX_INACTIVE_INTERVAL_KEY, + this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, + getCreationTime().toEpochMilli()); + this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds()); - this.delta.put(LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli()); + this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, + getLastAccessedTime().toEpochMilli()); this.isNew = true; this.flushImmediateIfNecessary(); } @@ -266,7 +244,8 @@ public class ReactiveRedisOperationsSessionRepository implements @Override public void setLastAccessedTime(Instant lastAccessedTime) { this.cached.setLastAccessedTime(lastAccessedTime); - putAndFlush(LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli()); + putAndFlush(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, + getLastAccessedTime().toEpochMilli()); } @Override @@ -277,7 +256,7 @@ public class ReactiveRedisOperationsSessionRepository implements @Override public void setMaxInactiveInterval(Duration interval) { this.cached.setMaxInactiveInterval(interval); - putAndFlush(MAX_INACTIVE_INTERVAL_KEY, + putAndFlush(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds()); } @@ -354,36 +333,4 @@ public class ReactiveRedisOperationsSessionRepository implements } - private static final class SessionMapper - implements Function, MapSession> { - - private final String id; - - private SessionMapper(String id) { - this.id = id; - } - - @Override - public MapSession apply(Map map) { - MapSession session = new MapSession(this.id); - - session.setCreationTime( - Instant.ofEpochMilli((long) map.get(CREATION_TIME_KEY))); - session.setLastAccessedTime( - Instant.ofEpochMilli((long) map.get(LAST_ACCESSED_TIME_KEY))); - session.setMaxInactiveInterval( - Duration.ofSeconds((int) map.get(MAX_INACTIVE_INTERVAL_KEY))); - - map.forEach((name, value) -> { - if (name.startsWith(ATTRIBUTE_PREFIX)) { - session.setAttribute(name.substring(ATTRIBUTE_PREFIX.length()), - value); - } - }); - - return session; - } - - } - } diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java index 2be2796d..2201817e 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java @@ -264,33 +264,6 @@ public class RedisOperationsSessionRepository implements */ public static final String DEFAULT_NAMESPACE = "spring:session"; - /** - * The key in the Hash representing - * {@link org.springframework.session.Session#getCreationTime()}. - */ - static final String CREATION_TIME_ATTR = "creationTime"; - - /** - * The key in the Hash representing - * {@link org.springframework.session.Session#getMaxInactiveInterval()} - * . - */ - static final String MAX_INACTIVE_ATTR = "maxInactiveInterval"; - - /** - * The key in the Hash representing - * {@link org.springframework.session.Session#getLastAccessedTime()}. - */ - static final String LAST_ACCESSED_ATTR = "lastAccessedTime"; - - /** - * The prefix of the key used for session attributes. The suffix is the name of - * the session attribute. For example, if the session contained an attribute named - * attributeName, then there would be an entry in the hash named - * sessionAttr:attributeName that mapped to its value. - */ - static final String SESSION_ATTR_PREFIX = "sessionAttr:"; - private int database = RedisOperationsSessionRepository.DEFAULT_DATABASE; /** @@ -480,17 +453,18 @@ public class RedisOperationsSessionRepository implements MapSession loaded = new MapSession(id); for (Map.Entry entry : entries.entrySet()) { String key = (String) entry.getKey(); - if (CREATION_TIME_ATTR.equals(key)) { + if (RedisSessionMapper.CREATION_TIME_KEY.equals(key)) { loaded.setCreationTime(Instant.ofEpochMilli((long) entry.getValue())); } - else if (MAX_INACTIVE_ATTR.equals(key)) { + else if (RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY.equals(key)) { loaded.setMaxInactiveInterval(Duration.ofSeconds((int) entry.getValue())); } - else if (LAST_ACCESSED_ATTR.equals(key)) { + else if (RedisSessionMapper.LAST_ACCESSED_TIME_KEY.equals(key)) { loaded.setLastAccessedTime(Instant.ofEpochMilli((long) entry.getValue())); } - else if (key.startsWith(SESSION_ATTR_PREFIX)) { - loaded.setAttribute(key.substring(SESSION_ATTR_PREFIX.length()), + else if (key.startsWith(RedisSessionMapper.ATTRIBUTE_PREFIX)) { + loaded.setAttribute( + key.substring(RedisSessionMapper.ATTRIBUTE_PREFIX.length()), entry.getValue()); } } @@ -689,7 +663,7 @@ public class RedisOperationsSessionRepository implements * @return the attribute key name */ static String getSessionAttrNameKey(String attributeName) { - return SESSION_ATTR_PREFIX + attributeName; + return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName; } /** @@ -719,9 +693,12 @@ public class RedisOperationsSessionRepository implements RedisSession(Duration maxInactiveInterval) { this(new MapSession()); this.cached.setMaxInactiveInterval(maxInactiveInterval); - this.delta.put(CREATION_TIME_ATTR, getCreationTime().toEpochMilli()); - this.delta.put(MAX_INACTIVE_ATTR, (int) getMaxInactiveInterval().getSeconds()); - this.delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime().toEpochMilli()); + 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; } @@ -745,7 +722,8 @@ public class RedisOperationsSessionRepository implements @Override public void setLastAccessedTime(Instant lastAccessedTime) { this.cached.setLastAccessedTime(lastAccessedTime); - this.putAndFlush(LAST_ACCESSED_ATTR, getLastAccessedTime().toEpochMilli()); + this.putAndFlush(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, + getLastAccessedTime().toEpochMilli()); } @Override @@ -780,7 +758,8 @@ public class RedisOperationsSessionRepository implements @Override public void setMaxInactiveInterval(Duration interval) { this.cached.setMaxInactiveInterval(interval); - this.putAndFlush(MAX_INACTIVE_ATTR, (int) getMaxInactiveInterval().getSeconds()); + this.putAndFlush(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, + (int) getMaxInactiveInterval().getSeconds()); } @Override diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java new file mode 100644 index 00000000..476d03d3 --- /dev/null +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java @@ -0,0 +1,97 @@ +/* + * Copyright 2014-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.redis; + +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.function.Function; + +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}. + * + * @author Vedran Pavic + * @since 2.2.0 + */ +final class RedisSessionMapper implements Function, MapSession> { + + /** + * The key in the hash representing {@link Session#getCreationTime()}. + */ + static final String CREATION_TIME_KEY = "creationTime"; + + /** + * The key in the hash representing {@link Session#getLastAccessedTime()}. + */ + static final String LAST_ACCESSED_TIME_KEY = "lastAccessedTime"; + + /** + * The key in the hash representing {@link Session#getMaxInactiveInterval()}. + */ + static final String MAX_INACTIVE_INTERVAL_KEY = "maxInactiveInterval"; + + /** + * The prefix of the key in the hash used for session attributes. For example, if the + * session contained an attribute named {@code attributeName}, then there would be an + * entry in the hash named {@code sessionAttr:attributeName} that mapped to its value. + */ + static final String ATTRIBUTE_PREFIX = "sessionAttr:"; + + private final String sessionId; + + RedisSessionMapper(String sessionId) { + Assert.hasText(sessionId, "sessionId must not be empty"); + this.sessionId = sessionId; + } + + @Override + public MapSession apply(Map map) { + Assert.notEmpty(map, "map must not be empty"); + MapSession session = new MapSession(this.sessionId); + Long creationTime = (Long) map.get(CREATION_TIME_KEY); + if (creationTime == null) { + handleMissingKey(CREATION_TIME_KEY); + } + session.setCreationTime(Instant.ofEpochMilli(creationTime)); + Long lastAccessedTime = (Long) map.get(LAST_ACCESSED_TIME_KEY); + if (lastAccessedTime == null) { + handleMissingKey(LAST_ACCESSED_TIME_KEY); + } + session.setLastAccessedTime(Instant.ofEpochMilli(lastAccessedTime)); + Integer maxInactiveInterval = (Integer) map.get(MAX_INACTIVE_INTERVAL_KEY); + if (maxInactiveInterval == null) { + handleMissingKey(MAX_INACTIVE_INTERVAL_KEY); + } + session.setMaxInactiveInterval(Duration.ofSeconds(maxInactiveInterval)); + map.forEach((name, value) -> { + if (name.startsWith(ATTRIBUTE_PREFIX)) { + session.setAttribute(name.substring(ATTRIBUTE_PREFIX.length()), value); + } + }); + return session; + } + + private static void handleMissingKey(String key) { + throw new IllegalStateException(key + " key must not be null"); + } + +} diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepositoryTests.java index ab884701..472e939f 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisOperationsSessionRepositoryTests.java @@ -167,15 +167,12 @@ public class ReactiveRedisOperationsSessionRepositoryTests { Map delta = this.delta.getAllValues().get(0); assertThat(delta.size()).isEqualTo(3); - assertThat(delta.get(ReactiveRedisOperationsSessionRepository.CREATION_TIME_KEY)) + assertThat(delta.get(RedisSessionMapper.CREATION_TIME_KEY)) .isEqualTo(newSession.getCreationTime().toEpochMilli()); - assertThat(delta - .get(ReactiveRedisOperationsSessionRepository.MAX_INACTIVE_INTERVAL_KEY)) - .isEqualTo( - (int) newSession.getMaxInactiveInterval().getSeconds()); - assertThat(delta - .get(ReactiveRedisOperationsSessionRepository.LAST_ACCESSED_TIME_KEY)) - .isEqualTo(newSession.getLastAccessedTime().toEpochMilli()); + assertThat(delta.get(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY)) + .isEqualTo((int) newSession.getMaxInactiveInterval().getSeconds()); + assertThat(delta.get(RedisSessionMapper.LAST_ACCESSED_TIME_KEY)) + .isEqualTo(newSession.getLastAccessedTime().toEpochMilli()); } @Test @@ -214,7 +211,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests { verifyZeroInteractions(this.hashOperations); assertThat(this.delta.getAllValues().get(0)) - .isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + .isEqualTo(map(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, session.getLastAccessedTime().toEpochMilli())); } @@ -316,16 +313,14 @@ public class ReactiveRedisOperationsSessionRepositoryTests { expected.setLastAccessedTime(Instant.now().minusSeconds(60)); expected.setAttribute(attribute1, "test"); expected.setAttribute(attribute2, null); - Map map = map( - ReactiveRedisOperationsSessionRepository.ATTRIBUTE_PREFIX + attribute1, + Map map = map(RedisSessionMapper.ATTRIBUTE_PREFIX + attribute1, expected.getAttribute(attribute1), - ReactiveRedisOperationsSessionRepository.ATTRIBUTE_PREFIX + attribute2, - expected.getAttribute(attribute2), - ReactiveRedisOperationsSessionRepository.CREATION_TIME_KEY, + RedisSessionMapper.ATTRIBUTE_PREFIX + attribute2, + expected.getAttribute(attribute2), RedisSessionMapper.CREATION_TIME_KEY, expected.getCreationTime().toEpochMilli(), - ReactiveRedisOperationsSessionRepository.MAX_INACTIVE_INTERVAL_KEY, + RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) expected.getMaxInactiveInterval().getSeconds(), - ReactiveRedisOperationsSessionRepository.LAST_ACCESSED_TIME_KEY, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, expected.getLastAccessedTime().toEpochMilli()); given(this.hashOperations.entries(anyString())) .willReturn(Flux.fromIterable(map.entrySet())); @@ -360,9 +355,9 @@ public class ReactiveRedisOperationsSessionRepositoryTests { @SuppressWarnings("unchecked") public void getSessionExpired() { given(this.redisOperations.opsForHash()).willReturn(this.hashOperations); - Map map = map(ReactiveRedisOperationsSessionRepository.CREATION_TIME_KEY, 0L, - ReactiveRedisOperationsSessionRepository.MAX_INACTIVE_INTERVAL_KEY, 1, - ReactiveRedisOperationsSessionRepository.LAST_ACCESSED_TIME_KEY, + Map map = map(RedisSessionMapper.CREATION_TIME_KEY, 0L, + RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli()); given(this.hashOperations.entries(anyString())) .willReturn(Flux.fromIterable(map.entrySet())); diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java index 43b584fa..ede229b2 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java @@ -190,14 +190,12 @@ public class RedisOperationsSessionRepositoryTests { Map delta = getDelta(); assertThat(delta.size()).isEqualTo(3); - Object creationTime = delta - .get(RedisOperationsSessionRepository.CREATION_TIME_ATTR); + Object creationTime = delta.get(RedisSessionMapper.CREATION_TIME_KEY); assertThat(creationTime).isEqualTo(session.getCreationTime().toEpochMilli()); - assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)) - .isEqualTo((int) Duration - .ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS) + assertThat(delta.get(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY)).isEqualTo( + (int) Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS) .getSeconds()); - assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR)) + assertThat(delta.get(RedisSessionMapper.LAST_ACCESSED_TIME_KEY)) .isEqualTo(session.getCreationTime().toEpochMilli()); } @@ -283,9 +281,8 @@ public class RedisOperationsSessionRepositoryTests { this.redisRepository.save(session); - assertThat(getDelta()) - .isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, - session.getLastAccessedTime().toEpochMilli())); + assertThat(getDelta()).isEqualTo(map(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, + session.getLastAccessedTime().toEpochMilli())); } @Test @@ -364,12 +361,11 @@ public class RedisOperationsSessionRepositoryTests { given(this.redisOperations.boundSetOps(anyString())) .willReturn(this.boundSetOperations); Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), - expected.getAttribute(attrName), - RedisOperationsSessionRepository.CREATION_TIME_ATTR, + expected.getAttribute(attrName), RedisSessionMapper.CREATION_TIME_KEY, expected.getCreationTime().toEpochMilli(), - RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, + RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) expected.getMaxInactiveInterval().getSeconds(), - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, expected.getLastAccessedTime().toEpochMilli()); given(this.boundHashOperations.entries()).willReturn(map); given(this.redisOperations.boundSetOps(anyString())) @@ -378,7 +374,7 @@ public class RedisOperationsSessionRepositoryTests { String id = expected.getId(); this.redisRepository.deleteById(id); - assertThat(getDelta().get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)) + assertThat(getDelta().get(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY)) .isEqualTo(0); verify(this.redisOperations, atLeastOnce()).delete(getKey("expires:" + id)); verify(this.redisOperations, never()).boundValueOps(getKey("expires:" + id)); @@ -418,12 +414,11 @@ public class RedisOperationsSessionRepositoryTests { Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attribute1), expected.getAttribute(attribute1), RedisOperationsSessionRepository.getSessionAttrNameKey(attribute2), - expected.getAttribute(attribute2), - RedisOperationsSessionRepository.CREATION_TIME_ATTR, + expected.getAttribute(attribute2), RedisSessionMapper.CREATION_TIME_KEY, expected.getCreationTime().toEpochMilli(), - RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, + RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) expected.getMaxInactiveInterval().getSeconds(), - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, expected.getLastAccessedTime().toEpochMilli()); given(this.boundHashOperations.entries()).willReturn(map); @@ -447,8 +442,8 @@ public class RedisOperationsSessionRepositoryTests { String expiredId = "expired-id"; given(this.redisOperations.boundHashOps(getKey(expiredId))) .willReturn(this.boundHashOperations); - Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1, - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli()); given(this.boundHashOperations.entries()).willReturn(map); @@ -464,8 +459,8 @@ public class RedisOperationsSessionRepositoryTests { .willReturn(Collections.singleton(expiredId)); given(this.redisOperations.boundHashOps(getKey(expiredId))) .willReturn(this.boundHashOperations); - Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1, - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli()); given(this.boundHashOperations.entries()).willReturn(map); @@ -486,11 +481,9 @@ public class RedisOperationsSessionRepositoryTests { .willReturn(Collections.singleton(sessionId)); given(this.redisOperations.boundHashOps(getKey(sessionId))) .willReturn(this.boundHashOperations); - Map map = map(RedisOperationsSessionRepository.CREATION_TIME_ATTR, - createdTime.toEpochMilli(), - RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, - (int) maxInactive.getSeconds(), - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + Map map = map(RedisSessionMapper.CREATION_TIME_KEY, createdTime.toEpochMilli(), + RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, + (int) maxInactive.getSeconds(), RedisSessionMapper.LAST_ACCESSED_TIME_KEY, lastAccessed.toEpochMilli()); given(this.boundHashOperations.entries()).willReturn(map); @@ -572,8 +565,8 @@ public class RedisOperationsSessionRepositoryTests { String deletedId = "deleted-id"; given(this.redisOperations.boundHashOps(getKey(deletedId))) .willReturn(this.boundHashOperations); - Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 0, - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5)); given(this.boundHashOperations.entries()).willReturn(map); @@ -625,8 +618,8 @@ public class RedisOperationsSessionRepositoryTests { String expiredId = "expired-id"; given(this.redisOperations.boundHashOps(getKey(expiredId))) .willReturn(this.boundHashOperations); - Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1, - RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, + Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, + RedisSessionMapper.LAST_ACCESSED_TIME_KEY, System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5)); given(this.boundHashOperations.entries()).willReturn(map); @@ -753,14 +746,12 @@ public class RedisOperationsSessionRepositoryTests { Map delta = getDelta(); assertThat(delta.size()).isEqualTo(3); - Object creationTime = delta - .get(RedisOperationsSessionRepository.CREATION_TIME_ATTR); + Object creationTime = delta.get(RedisSessionMapper.CREATION_TIME_KEY); assertThat(creationTime).isEqualTo(session.getCreationTime().toEpochMilli()); - assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)) - .isEqualTo((int) Duration - .ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS) + assertThat(delta.get(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY)).isEqualTo( + (int) Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS) .getSeconds()); - assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR)) + assertThat(delta.get(RedisSessionMapper.LAST_ACCESSED_TIME_KEY)) .isEqualTo(session.getCreationTime().toEpochMilli()); } @@ -777,8 +768,7 @@ public class RedisOperationsSessionRepositoryTests { this.redisRepository.createSession(); Map delta = getDelta(); assertThat(delta.size()).isEqualTo(3); - assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)) - .isEqualTo(60); + assertThat(delta.get(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY)).isEqualTo(60); } @Test @@ -858,9 +848,8 @@ public class RedisOperationsSessionRepositoryTests { Map delta = getDelta(2); assertThat(delta.size()).isEqualTo(1); - assertThat(delta) - .isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, - session.getLastAccessedTime().toEpochMilli())); + assertThat(delta).isEqualTo(map(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, + session.getLastAccessedTime().toEpochMilli())); } @Test diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionMapperTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionMapperTests.java new file mode 100644 index 00000000..5640c6c3 --- /dev/null +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionMapperTests.java @@ -0,0 +1,121 @@ +/* + * Copyright 2014-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.redis; + +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.session.MapSession; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link RedisSessionMapper}. + * + * @author Vedran Pavic + */ +class RedisSessionMapperTests { + + private RedisSessionMapper mapper; + + @BeforeEach + void setUp() { + this.mapper = new RedisSessionMapper("id"); + } + + @Test + void constructor_NullId_ShouldThrowException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new RedisSessionMapper(null)) + .withMessage("sessionId must not be empty"); + } + + @Test + void constructor_EmptyId_ShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> new RedisSessionMapper(" ")) + .withMessage("sessionId must not be empty"); + } + + @Test + void apply_NullMap_ShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.mapper.apply(null)) + .withMessage("map must not be empty"); + } + + @Test + void apply_EmptyMap_ShouldThrowException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> this.mapper.apply(Collections.emptyMap())) + .withMessage("map must not be empty"); + } + + @Test + void apply_MapWithoutCreationTime_ShouldThrowException() { + Map sessionMap = new HashMap<>(); + sessionMap.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, 0L); + sessionMap.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1800); + assertThatIllegalStateException().isThrownBy(() -> this.mapper.apply(sessionMap)) + .withMessage( + RedisSessionMapper.CREATION_TIME_KEY + " key must not be null"); + } + + @Test + void apply_MapWithoutLastAccessedTime_ShouldThrowException() { + Map sessionMap = new HashMap<>(); + sessionMap.put(RedisSessionMapper.CREATION_TIME_KEY, 0L); + sessionMap.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1800); + assertThatIllegalStateException().isThrownBy(() -> this.mapper.apply(sessionMap)) + .withMessage(RedisSessionMapper.LAST_ACCESSED_TIME_KEY + + " key must not be null"); + } + + @Test + void apply_MapWithoutMaxInactiveInterval_ShouldThrowException() { + Map sessionMap = new HashMap<>(); + sessionMap.put(RedisSessionMapper.CREATION_TIME_KEY, 0L); + sessionMap.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, 0L); + assertThatIllegalStateException().isThrownBy(() -> this.mapper.apply(sessionMap)) + .withMessage(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY + + " key must not be null"); + } + + @Test + void apply_ValidMap_ShouldReturnSession() { + Map sessionMap = new HashMap<>(); + sessionMap.put(RedisSessionMapper.CREATION_TIME_KEY, 0L); + sessionMap.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, 0L); + sessionMap.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1800); + sessionMap.put(RedisSessionMapper.ATTRIBUTE_PREFIX + "existing", "value"); + sessionMap.put(RedisSessionMapper.ATTRIBUTE_PREFIX + "missing", null); + MapSession session = this.mapper.apply(sessionMap); + assertThat(session.getId()).isEqualTo("id"); + assertThat(session.getCreationTime()).isEqualTo(Instant.ofEpochMilli(0)); + assertThat(session.getLastAccessedTime()).isEqualTo(Instant.ofEpochMilli(0)); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(30)); + assertThat(session.getAttributeNames()).hasSize(1); + assertThat((String) session.getAttribute("existing")).isEqualTo("value"); + } + +}