Align Spring Session MongoDB with WebSession API.

Introduce new state to MongoSession denoting if a session is new or not. Only perform a save on a non-new MongoSession if it already exists in the MongoDB. Otherwise, throw an IllegalStateException.

Resolved #47.
This commit is contained in:
Greg Turnquist
2019-01-16 09:49:58 -06:00
parent b04a0305e9
commit 4c4bb6ac19
5 changed files with 132 additions and 23 deletions

View File

@@ -95,7 +95,19 @@ public class MongoOperationsSessionRepository
@Override
public void save(MongoSession session) {
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
if (session.isNew()) {
session.setNew(false);
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
} else {
if (findSession(session.getId()) == null) {
throw new IllegalStateException("Session was invalidated");
} else {
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
}
}
}
@Override

View File

@@ -51,6 +51,7 @@ public class MongoSession implements Session {
private long intervalSeconds;
@Getter @Setter private Date expireAt;
private Map<String, Object> attrs = new HashMap<>();
private boolean isNew = true;
public MongoSession() {
this(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
@@ -129,6 +130,14 @@ public class MongoSession implements Session {
return this.intervalSeconds >= 0 && new Date().after(this.expireAt);
}
public void setNew(boolean isNew) {
this.isNew = isNew;
}
public boolean isNew() {
return this.isNew;
}
static String coverDot(String attributeName) {
return attributeName.replace('.', DOT_COVER_CHAR);
}

View File

@@ -107,9 +107,18 @@ public class ReactiveMongoOperationsSessionRepository
@Override
public Mono<Void> save(MongoSession session) {
return this.mongoOperations
.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName)
.then();
if (session.isNew()) {
session.setNew(false);
return this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName)
.then();
} else {
return findSession(session.getId())
.map(document -> this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName))
.switchIfEmpty(Mono.error(new IllegalStateException("Session was invalidated")))
.then();
}
}
/**

View File

@@ -62,36 +62,42 @@ public class MongoOperationsSessionRepositoryTest {
private MongoOperationsSessionRepository repository;
@Before
public void setUp() throws Exception {
public void setUp() {
this.repository = new MongoOperationsSessionRepository(this.mongoOperations);
this.repository.setMongoSessionConverter(this.converter);
}
@Test
public void shouldCreateSession() throws Exception {
public void shouldCreateSession() {
// when
MongoSession session = this.repository.createSession();
// then
assertThat(session.getId()).isNotEmpty();
assertThat(session.isNew()).isTrue();
assertThat(session.getMaxInactiveInterval().getSeconds())
.isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
}
@Test
public void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() throws Exception {
public void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() {
// when
this.repository.setMaxInactiveIntervalInSeconds(null);
MongoSession session = this.repository.createSession();
// then
assertThat(session.getId()).isNotEmpty();
assertThat(session.isNew()).isTrue();
assertThat(session.getMaxInactiveInterval().getSeconds())
.isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
}
@Test
public void shouldSaveSession() throws Exception {
public void shouldSaveNewSession() {
// given
MongoSession session = new MongoSession();
BasicDBObject dbSession = new BasicDBObject();
@@ -99,6 +105,44 @@ public class MongoOperationsSessionRepositoryTest {
given(this.converter.convert(session,
TypeDescriptor.valueOf(MongoSession.class),
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
// when
this.repository.save(session);
// then
verify(this.mongoOperations).save(dbSession, MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME);
assertThat(session.isNew()).isFalse();
}
@Test
public void shouldHandleInvalidatedSession() {
MongoSession session = new MongoSession();
session.setNew(false);
assertThatIllegalStateException().isThrownBy(() -> {
this.repository.save(session);
}).withMessage("Session was invalidated");
}
@Test
public void shouldSaveExistingSession() {
// given
MongoSession session = new MongoSession();
session.setNew(false);
BasicDBObject dbSession = new BasicDBObject();
Document sessionDocument = new Document();
given(this.mongoOperations.findById(session.getId(), Document.class,
MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(sessionDocument);
given(this.converter.convert(session,
TypeDescriptor.valueOf(MongoSession.class),
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
// when
this.repository.save(session);
@@ -107,7 +151,8 @@ public class MongoOperationsSessionRepositoryTest {
}
@Test
public void shouldGetSession() throws Exception {
public void shouldGetSession() {
// given
String sessionId = UUID.randomUUID().toString();
Document sessionDocument = new Document();
@@ -128,7 +173,8 @@ public class MongoOperationsSessionRepositoryTest {
}
@Test
public void shouldHandleExpiredSession() throws Exception {
public void shouldHandleExpiredSession() {
// given
String sessionId = UUID.randomUUID().toString();
Document sessionDocument = new Document();
@@ -152,7 +198,8 @@ public class MongoOperationsSessionRepositoryTest {
}
@Test
public void shouldDeleteSession() throws Exception {
public void shouldDeleteSession() {
// given
String sessionId = UUID.randomUUID().toString();
@@ -175,7 +222,8 @@ public class MongoOperationsSessionRepositoryTest {
}
@Test
public void shouldGetSessionsMapByPrincipal() throws Exception {
public void shouldGetSessionsMapByPrincipal() {
// given
String principalNameIndexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
@@ -203,7 +251,8 @@ public class MongoOperationsSessionRepositoryTest {
}
@Test
public void shouldReturnEmptyMapForNotSupportedIndex() throws Exception {
public void shouldReturnEmptyMapForNotSupportedIndex() {
// given
String index = "some_not_supported_index_name";

View File

@@ -23,6 +23,7 @@ import static org.mockito.BDDMockito.*;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME;
import java.util.UUID;
@@ -84,6 +85,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
.as(StepVerifier::create)
.expectNextMatches(mongoSession -> {
assertThat(mongoSession.getId()).isNotEmpty();
assertThat(mongoSession.isNew()).isTrue();
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
.isEqualTo(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
return true;
@@ -102,6 +104,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
.as(StepVerifier::create)
.expectNextMatches(mongoSession -> {
assertThat(mongoSession.getId()).isNotEmpty();
assertThat(mongoSession.isNew()).isTrue();
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
.isEqualTo(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
return true;
@@ -114,8 +117,12 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
// given
MongoSession session = new MongoSession();
Document sessionDocument = new Document();
BasicDBObject dbSession = new BasicDBObject();
given(this.mongoOperations.findById(session.getId(), Document.class,
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
given(this.converter.convert(session,
TypeDescriptor.valueOf(MongoSession.class),
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
@@ -127,7 +134,30 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
.as(StepVerifier::create)
.verifyComplete();
verify(this.mongoOperations).save(dbSession, ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME);
assertThat(session.isNew()).isFalse();
verify(this.mongoOperations).save(dbSession, DEFAULT_COLLECTION_NAME);
verifyNoMoreInteractions(this.mongoOperations);
}
@Test
public void shouldCreateAnErrorWhenSavingSessionNotInMongo() {
// given
MongoSession session = new MongoSession();
session.setNew(false);
given(this.mongoOperations.findById(session.getId(), Document.class,
DEFAULT_COLLECTION_NAME)).willReturn(Mono.empty());
// when
this.repository.save(session)
.as(StepVerifier::create)
.verifyErrorMessage("Session was invalidated");
assertThat(session.isNew()).isFalse();
verify(this.mongoOperations).findById(session.getId(), Document.class, DEFAULT_COLLECTION_NAME);
verifyNoMoreInteractions(this.mongoOperations);
}
@Test
@@ -138,7 +168,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
Document sessionDocument = new Document();
given(this.mongoOperations.findById(sessionId, Document.class,
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
MongoSession session = new MongoSession();
@@ -160,9 +190,9 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
Document sessionDocument = new Document();
given(this.mongoOperations.findById(sessionId, Document.class,
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
given(this.mongoOperations.remove(sessionDocument, ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME))
given(this.mongoOperations.remove(sessionDocument, DEFAULT_COLLECTION_NAME))
.willReturn(Mono.just(DeleteResult.acknowledged(1)));
MongoSession session = mock(MongoSession.class);
@@ -177,8 +207,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
.verifyComplete();
// then
verify(this.mongoOperations).remove(any(Document.class),
eq(ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
verify(this.mongoOperations).remove(any(Document.class), eq(DEFAULT_COLLECTION_NAME));
}
@Test
@@ -189,7 +218,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
Document sessionDocument = new Document();
given(this.mongoOperations.findById(sessionId, Document.class,
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
given(this.mongoOperations.remove(sessionDocument, "sessions"))
.willReturn(Mono.just(DeleteResult.acknowledged(1)));
@@ -204,9 +233,7 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
.as(StepVerifier::create)
.verifyComplete();
verify(this.mongoOperations).remove(any(Document.class),
eq(ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
verify(this.mongoOperations).remove(any(Document.class), eq(DEFAULT_COLLECTION_NAME));
verify(this.eventPublisher).publishEvent(any(SessionDeletedEvent.class));
}
@@ -225,5 +252,8 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
// then
verify(this.blockingMongoOperations, times(1)).indexOps((String) any());
verify(this.converter, times(1)).ensureIndexes(indexOperations);
verifyNoMoreInteractions(this.blockingMongoOperations);
verifyNoMoreInteractions(this.converter);
}
}