diff --git a/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java b/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java index 3d802c7..e0a4960 100644 --- a/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java +++ b/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java @@ -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 diff --git a/src/main/java/org/springframework/session/data/mongo/MongoSession.java b/src/main/java/org/springframework/session/data/mongo/MongoSession.java index 6dc91a8..9edfa8a 100644 --- a/src/main/java/org/springframework/session/data/mongo/MongoSession.java +++ b/src/main/java/org/springframework/session/data/mongo/MongoSession.java @@ -51,6 +51,7 @@ public class MongoSession implements Session { private long intervalSeconds; @Getter @Setter private Date expireAt; private Map 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); } diff --git a/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java b/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java index 83fa6b5..4f80572 100644 --- a/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java +++ b/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java @@ -107,9 +107,18 @@ public class ReactiveMongoOperationsSessionRepository @Override public Mono 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(); + } } /** diff --git a/src/test/java/org/springframework/session/data/mongo/MongoOperationsSessionRepositoryTest.java b/src/test/java/org/springframework/session/data/mongo/MongoOperationsSessionRepositoryTest.java index 5665327..e445cf7 100644 --- a/src/test/java/org/springframework/session/data/mongo/MongoOperationsSessionRepositoryTest.java +++ b/src/test/java/org/springframework/session/data/mongo/MongoOperationsSessionRepositoryTest.java @@ -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"; diff --git a/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java b/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java index c6a3aa6..dd9c80f 100644 --- a/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java +++ b/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java @@ -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); } }