Add MapReactorSessionRepository
Fixes gh-815
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2014-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A {@link SessionRepository} backed by a {@link Map} and that uses a
|
||||
* {@link MapSession}. By default a {@link ConcurrentHashMap} is
|
||||
* used, but a custom {@link Map} can be injected to use distributed maps
|
||||
* provided by NoSQL stores like Redis and Hazelcast.
|
||||
*
|
||||
* <p>
|
||||
* The implementation does NOT support firing {@link SessionDeletedEvent} or
|
||||
* {@link SessionExpiredEvent}.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactorSessionRepository implements ReactorSessionRepository<Session> {
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private final Map<String, Session> sessions;
|
||||
|
||||
/**
|
||||
* Creates an instance backed by a {@link ConcurrentHashMap}.
|
||||
*/
|
||||
public MapReactorSessionRepository() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Map<String, Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Session... sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
for(Session session : sessions) {
|
||||
this.performSave(session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Iterable<Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
for(Session session : sessions) {
|
||||
this.performSave(session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);
|
||||
}
|
||||
|
||||
public Mono<Void> save(Session session) {
|
||||
return Mono.fromRunnable(() -> performSave(session));
|
||||
}
|
||||
|
||||
private void performSave(Session session) {
|
||||
this.sessions.put(session.getId(), new MapSession(session));
|
||||
}
|
||||
|
||||
public Mono<Session> findById(String id) {
|
||||
return Mono.defer(() -> {
|
||||
Session saved = this.sessions.get(id);
|
||||
if (saved == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
if (saved.isExpired()) {
|
||||
delete(saved.getId());
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.just(new MapSession(saved));
|
||||
});
|
||||
}
|
||||
|
||||
public Mono<Void> delete(String id) {
|
||||
return Mono.fromRunnable(() -> this.sessions.remove(id));
|
||||
}
|
||||
|
||||
public Mono<Session> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
Session result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
return Mono.just(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2014-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactorSessionRepositoryTests {
|
||||
MapReactorSessionRepository repository;
|
||||
|
||||
MapSession session;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository = new MapReactorSessionRepository();
|
||||
this.session = new MapSession("session-id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorVarargsThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(this.session);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorVarargsWhenNullThenThrowsIllegalArgumentException() {
|
||||
Session[] sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorIterableThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(Arrays.asList(this.session));
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorIterableWhenNullThenThrowsIllegalArgumentException() {
|
||||
Iterable<Session> sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMapThenFound() {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(this.session.getId(), this.session);
|
||||
this.repository = new MapReactorSessionRepository(sessions);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
Map<String,Session> sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenNoSubscribersThenNotFound() {
|
||||
this.repository.save(this.session);
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenSubscriberThenFound() {
|
||||
this.repository.save(this.session).block();
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenNotExpiredThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(this.session);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenExpiredThenEmpty() {
|
||||
this.session.setMaxInactiveInterval(Duration.ofSeconds(1));
|
||||
this.session.setLastAccessedTime(Instant.now().minus(5, ChronoUnit.MINUTES));
|
||||
this.repository = new MapReactorSessionRepository(Arrays.asList(this.session));
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenDefaultMaxInactiveIntervalThenDefaultMaxInactiveInterval() {
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session).isInstanceOf(MapSession.class);
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(
|
||||
(int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expectedMaxInterval);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user