Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Commit 4c4bb6a

Browse files
committed
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.
1 parent b04a030 commit 4c4bb6a

File tree

5 files changed

+132
-23
lines changed

5 files changed

+132
-23
lines changed

src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,19 @@ public MongoSession createSession() {
9595

9696
@Override
9797
public void save(MongoSession session) {
98-
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
98+
99+
if (session.isNew()) {
100+
101+
session.setNew(false);
102+
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
103+
} else {
104+
105+
if (findSession(session.getId()) == null) {
106+
throw new IllegalStateException("Session was invalidated");
107+
} else {
108+
this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName);
109+
}
110+
}
99111
}
100112

101113
@Override

src/main/java/org/springframework/session/data/mongo/MongoSession.java

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class MongoSession implements Session {
5151
private long intervalSeconds;
5252
@Getter @Setter private Date expireAt;
5353
private Map<String, Object> attrs = new HashMap<>();
54+
private boolean isNew = true;
5455

5556
public MongoSession() {
5657
this(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
@@ -129,6 +130,14 @@ public boolean isExpired() {
129130
return this.intervalSeconds >= 0 && new Date().after(this.expireAt);
130131
}
131132

133+
public void setNew(boolean isNew) {
134+
this.isNew = isNew;
135+
}
136+
137+
public boolean isNew() {
138+
return this.isNew;
139+
}
140+
132141
static String coverDot(String attributeName) {
133142
return attributeName.replace('.', DOT_COVER_CHAR);
134143
}

src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,18 @@ public Mono<MongoSession> createSession() {
107107
@Override
108108
public Mono<Void> save(MongoSession session) {
109109

110-
return this.mongoOperations
111-
.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName)
112-
.then();
110+
if (session.isNew()) {
111+
112+
session.setNew(false);
113+
return this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName)
114+
.then();
115+
} else {
116+
117+
return findSession(session.getId())
118+
.map(document -> this.mongoOperations.save(convertToDBObject(this.mongoSessionConverter, session), this.collectionName))
119+
.switchIfEmpty(Mono.error(new IllegalStateException("Session was invalidated")))
120+
.then();
121+
}
113122
}
114123

115124
/**

src/test/java/org/springframework/session/data/mongo/MongoOperationsSessionRepositoryTest.java

+58-9
Original file line numberDiff line numberDiff line change
@@ -62,52 +62,97 @@ public class MongoOperationsSessionRepositoryTest {
6262
private MongoOperationsSessionRepository repository;
6363

6464
@Before
65-
public void setUp() throws Exception {
65+
public void setUp() {
66+
6667
this.repository = new MongoOperationsSessionRepository(this.mongoOperations);
6768
this.repository.setMongoSessionConverter(this.converter);
6869
}
6970

7071
@Test
71-
public void shouldCreateSession() throws Exception {
72+
public void shouldCreateSession() {
73+
7274
// when
7375
MongoSession session = this.repository.createSession();
7476

7577
// then
7678
assertThat(session.getId()).isNotEmpty();
79+
assertThat(session.isNew()).isTrue();
7780
assertThat(session.getMaxInactiveInterval().getSeconds())
7881
.isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
7982
}
8083

8184
@Test
82-
public void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() throws Exception {
85+
public void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() {
86+
8387
// when
8488
this.repository.setMaxInactiveIntervalInSeconds(null);
8589
MongoSession session = this.repository.createSession();
8690

8791
// then
8892
assertThat(session.getId()).isNotEmpty();
93+
assertThat(session.isNew()).isTrue();
8994
assertThat(session.getMaxInactiveInterval().getSeconds())
9095
.isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
9196
}
9297

9398
@Test
94-
public void shouldSaveSession() throws Exception {
99+
public void shouldSaveNewSession() {
100+
95101
// given
96102
MongoSession session = new MongoSession();
97103
BasicDBObject dbSession = new BasicDBObject();
98104

99105
given(this.converter.convert(session,
100106
TypeDescriptor.valueOf(MongoSession.class),
101107
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
108+
102109
// when
103110
this.repository.save(session);
104111

105112
// then
106113
verify(this.mongoOperations).save(dbSession, MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME);
114+
115+
assertThat(session.isNew()).isFalse();
107116
}
108117

109118
@Test
110-
public void shouldGetSession() throws Exception {
119+
public void shouldHandleInvalidatedSession() {
120+
121+
MongoSession session = new MongoSession();
122+
session.setNew(false);
123+
124+
assertThatIllegalStateException().isThrownBy(() -> {
125+
this.repository.save(session);
126+
}).withMessage("Session was invalidated");
127+
}
128+
129+
@Test
130+
public void shouldSaveExistingSession() {
131+
132+
// given
133+
MongoSession session = new MongoSession();
134+
session.setNew(false);
135+
BasicDBObject dbSession = new BasicDBObject();
136+
137+
Document sessionDocument = new Document();
138+
139+
given(this.mongoOperations.findById(session.getId(), Document.class,
140+
MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(sessionDocument);
141+
142+
given(this.converter.convert(session,
143+
TypeDescriptor.valueOf(MongoSession.class),
144+
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
145+
146+
// when
147+
this.repository.save(session);
148+
149+
// then
150+
verify(this.mongoOperations).save(dbSession, MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME);
151+
}
152+
153+
@Test
154+
public void shouldGetSession() {
155+
111156
// given
112157
String sessionId = UUID.randomUUID().toString();
113158
Document sessionDocument = new Document();
@@ -128,7 +173,8 @@ public void shouldGetSession() throws Exception {
128173
}
129174

130175
@Test
131-
public void shouldHandleExpiredSession() throws Exception {
176+
public void shouldHandleExpiredSession() {
177+
132178
// given
133179
String sessionId = UUID.randomUUID().toString();
134180
Document sessionDocument = new Document();
@@ -152,7 +198,8 @@ public void shouldHandleExpiredSession() throws Exception {
152198
}
153199

154200
@Test
155-
public void shouldDeleteSession() throws Exception {
201+
public void shouldDeleteSession() {
202+
156203
// given
157204
String sessionId = UUID.randomUUID().toString();
158205

@@ -175,7 +222,8 @@ public void shouldDeleteSession() throws Exception {
175222
}
176223

177224
@Test
178-
public void shouldGetSessionsMapByPrincipal() throws Exception {
225+
public void shouldGetSessionsMapByPrincipal() {
226+
179227
// given
180228
String principalNameIndexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
181229

@@ -203,7 +251,8 @@ public void shouldGetSessionsMapByPrincipal() throws Exception {
203251
}
204252

205253
@Test
206-
public void shouldReturnEmptyMapForNotSupportedIndex() throws Exception {
254+
public void shouldReturnEmptyMapForNotSupportedIndex() {
255+
207256
// given
208257
String index = "some_not_supported_index_name";
209258

src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java

+40-10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static org.mockito.BDDMockito.mock;
2424
import static org.mockito.BDDMockito.times;
2525
import static org.mockito.Mockito.verify;
26+
import static org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME;
2627

2728
import java.util.UUID;
2829

@@ -84,6 +85,7 @@ public void shouldCreateSession() {
8485
.as(StepVerifier::create)
8586
.expectNextMatches(mongoSession -> {
8687
assertThat(mongoSession.getId()).isNotEmpty();
88+
assertThat(mongoSession.isNew()).isTrue();
8789
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
8890
.isEqualTo(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
8991
return true;
@@ -102,6 +104,7 @@ public void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() {
102104
.as(StepVerifier::create)
103105
.expectNextMatches(mongoSession -> {
104106
assertThat(mongoSession.getId()).isNotEmpty();
107+
assertThat(mongoSession.isNew()).isTrue();
105108
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
106109
.isEqualTo(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
107110
return true;
@@ -114,8 +117,12 @@ public void shouldSaveSession() {
114117

115118
// given
116119
MongoSession session = new MongoSession();
120+
Document sessionDocument = new Document();
117121
BasicDBObject dbSession = new BasicDBObject();
118122

123+
given(this.mongoOperations.findById(session.getId(), Document.class,
124+
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
125+
119126
given(this.converter.convert(session,
120127
TypeDescriptor.valueOf(MongoSession.class),
121128
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
@@ -127,7 +134,30 @@ public void shouldSaveSession() {
127134
.as(StepVerifier::create)
128135
.verifyComplete();
129136

130-
verify(this.mongoOperations).save(dbSession, ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME);
137+
assertThat(session.isNew()).isFalse();
138+
verify(this.mongoOperations).save(dbSession, DEFAULT_COLLECTION_NAME);
139+
verifyNoMoreInteractions(this.mongoOperations);
140+
}
141+
142+
@Test
143+
public void shouldCreateAnErrorWhenSavingSessionNotInMongo() {
144+
145+
// given
146+
MongoSession session = new MongoSession();
147+
session.setNew(false);
148+
149+
given(this.mongoOperations.findById(session.getId(), Document.class,
150+
DEFAULT_COLLECTION_NAME)).willReturn(Mono.empty());
151+
152+
// when
153+
this.repository.save(session)
154+
.as(StepVerifier::create)
155+
.verifyErrorMessage("Session was invalidated");
156+
157+
assertThat(session.isNew()).isFalse();
158+
159+
verify(this.mongoOperations).findById(session.getId(), Document.class, DEFAULT_COLLECTION_NAME);
160+
verifyNoMoreInteractions(this.mongoOperations);
131161
}
132162

133163
@Test
@@ -138,7 +168,7 @@ public void shouldGetSession() {
138168
Document sessionDocument = new Document();
139169

140170
given(this.mongoOperations.findById(sessionId, Document.class,
141-
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
171+
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
142172

143173
MongoSession session = new MongoSession();
144174

@@ -160,9 +190,9 @@ public void shouldHandleExpiredSession() {
160190
Document sessionDocument = new Document();
161191

162192
given(this.mongoOperations.findById(sessionId, Document.class,
163-
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
193+
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
164194

165-
given(this.mongoOperations.remove(sessionDocument, ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME))
195+
given(this.mongoOperations.remove(sessionDocument, DEFAULT_COLLECTION_NAME))
166196
.willReturn(Mono.just(DeleteResult.acknowledged(1)));
167197

168198
MongoSession session = mock(MongoSession.class);
@@ -177,8 +207,7 @@ public void shouldHandleExpiredSession() {
177207
.verifyComplete();
178208

179209
// then
180-
verify(this.mongoOperations).remove(any(Document.class),
181-
eq(ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
210+
verify(this.mongoOperations).remove(any(Document.class), eq(DEFAULT_COLLECTION_NAME));
182211
}
183212

184213
@Test
@@ -189,7 +218,7 @@ public void shouldDeleteSession() {
189218
Document sessionDocument = new Document();
190219

191220
given(this.mongoOperations.findById(sessionId, Document.class,
192-
ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
221+
DEFAULT_COLLECTION_NAME)).willReturn(Mono.just(sessionDocument));
193222

194223
given(this.mongoOperations.remove(sessionDocument, "sessions"))
195224
.willReturn(Mono.just(DeleteResult.acknowledged(1)));
@@ -204,9 +233,7 @@ public void shouldDeleteSession() {
204233
.as(StepVerifier::create)
205234
.verifyComplete();
206235

207-
verify(this.mongoOperations).remove(any(Document.class),
208-
eq(ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
209-
236+
verify(this.mongoOperations).remove(any(Document.class), eq(DEFAULT_COLLECTION_NAME));
210237
verify(this.eventPublisher).publishEvent(any(SessionDeletedEvent.class));
211238
}
212239

@@ -225,5 +252,8 @@ public void shouldInvokeMethodToCreateIndexesImperatively() {
225252
// then
226253
verify(this.blockingMongoOperations, times(1)).indexOps((String) any());
227254
verify(this.converter, times(1)).ensureIndexes(indexOperations);
255+
256+
verifyNoMoreInteractions(this.blockingMongoOperations);
257+
verifyNoMoreInteractions(this.converter);
228258
}
229259
}

0 commit comments

Comments
 (0)