Skip to content

Commit 8dac35c

Browse files
committed
Fix session event handling in HazelcastSessionRepository
Previously, invoking HttpServletRequest#changeSessionId on session backed by HazelcastSessionRepository generated generated invalid session destroyed and session created events. This was due to use of IMap#remove and IMap#set when handling the change session id. This commit improves change session id handling to prevent publishing invalid events by using IMap#delete instead of IMap#remove and keeping track of originally assigned session id. Closes gh-1077
1 parent 19b8583 commit 8dac35c

File tree

3 files changed

+105
-45
lines changed

3 files changed

+105
-45
lines changed

spring-session-core/src/main/java/org/springframework/session/MapSession.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ public String getId() {
122122
return this.id;
123123
}
124124

125-
String getOriginalId() {
125+
/**
126+
* Get the original session id.
127+
* @return the original session id
128+
* @see #changeSessionId()
129+
*/
130+
public String getOriginalId() {
126131
return this.originalId;
127132
}
128133

spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/config/annotation/web/http/EnableHazelcastHttpSessionEventsTests.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2017 the original author or authors.
2+
* Copyright 2014-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@
5252
* expected after each SessionEvent.
5353
*
5454
* @author Tommy Ludwig
55+
* @author Vedran Pavic
5556
*/
5657
@RunWith(SpringRunner.class)
5758
@ContextConfiguration
@@ -168,6 +169,24 @@ public void saveUpdatesTimeToLiveTest() throws InterruptedException {
168169
assertThat(this.repository.findById(sessionToUpdate.getId())).isNotNull();
169170
}
170171

172+
@Test // gh-1077
173+
public void changeSessionIdNoEventTest() throws InterruptedException {
174+
S sessionToSave = this.repository.createSession();
175+
sessionToSave.setMaxInactiveInterval(Duration.ofMinutes(30));
176+
177+
this.repository.save(sessionToSave);
178+
179+
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
180+
assertThat(this.registry.<SessionCreatedEvent>getEvent(sessionToSave.getId()))
181+
.isInstanceOf(SessionCreatedEvent.class);
182+
this.registry.clear();
183+
184+
sessionToSave.changeSessionId();
185+
this.repository.save(sessionToSave);
186+
187+
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isFalse();
188+
}
189+
171190
@Configuration
172191
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
173192
static class HazelcastSessionConfig {

spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -224,20 +224,30 @@ public HazelcastSession createSession() {
224224

225225
@Override
226226
public void save(HazelcastSession session) {
227-
if (!session.getId().equals(session.originalId)) {
228-
this.sessions.remove(session.originalId);
229-
session.originalId = session.getId();
230-
}
231227
if (session.isNew) {
232228
this.sessions.set(session.getId(), session.getDelegate(),
233229
session.getMaxInactiveInterval().getSeconds(), TimeUnit.SECONDS);
234230
}
235-
else if (session.changed) {
236-
this.sessions.executeOnKey(session.getId(),
237-
new SessionUpdateEntryProcessor(session.getLastAccessedTime(),
238-
session.getMaxInactiveInterval(), session.delta));
231+
else if (session.sessionIdChanged) {
232+
this.sessions.delete(session.originalId);
233+
session.originalId = session.getId();
234+
this.sessions.set(session.getId(), session.getDelegate(),
235+
session.getMaxInactiveInterval().getSeconds(), TimeUnit.SECONDS);
236+
}
237+
else if (session.hasChanges()) {
238+
SessionUpdateEntryProcessor entryProcessor = new SessionUpdateEntryProcessor();
239+
if (session.lastAccessedTimeChanged) {
240+
entryProcessor.setLastAccessedTime(session.getLastAccessedTime());
241+
}
242+
if (session.maxInactiveIntervalChanged) {
243+
entryProcessor.setMaxInactiveInterval(session.getMaxInactiveInterval());
244+
}
245+
if (!session.delta.isEmpty()) {
246+
entryProcessor.setDelta(session.delta);
247+
}
248+
this.sessions.executeOnKey(session.getId(), entryProcessor);
239249
}
240-
session.clearFlags();
250+
session.clearChangeFlags();
241251
}
242252

243253
@Override
@@ -275,10 +285,13 @@ public Map<String, HazelcastSession> findByIndexNameAndIndexValue(String indexNa
275285

276286
@Override
277287
public void entryAdded(EntryEvent<String, MapSession> event) {
278-
if (logger.isDebugEnabled()) {
279-
logger.debug("Session created with id: " + event.getValue().getId());
288+
MapSession session = event.getValue();
289+
if (session.getId().equals(session.getOriginalId())) {
290+
if (logger.isDebugEnabled()) {
291+
logger.debug("Session created with id: " + session.getId());
292+
}
293+
this.eventPublisher.publishEvent(new SessionCreatedEvent(this, session));
280294
}
281-
this.eventPublisher.publishEvent(new SessionCreatedEvent(this, event.getValue()));
282295
}
283296

284297
@Override
@@ -292,11 +305,13 @@ public void entryEvicted(EntryEvent<String, MapSession> event) {
292305

293306
@Override
294307
public void entryRemoved(EntryEvent<String, MapSession> event) {
295-
if (logger.isDebugEnabled()) {
296-
logger.debug("Session deleted with id: " + event.getOldValue().getId());
308+
MapSession session = event.getOldValue();
309+
if (session != null) {
310+
if (logger.isDebugEnabled()) {
311+
logger.debug("Session deleted with id: " + session.getId());
312+
}
313+
this.eventPublisher.publishEvent(new SessionDeletedEvent(this, session));
297314
}
298-
this.eventPublisher
299-
.publishEvent(new SessionDeletedEvent(this, event.getOldValue()));
300315
}
301316

302317
/**
@@ -311,7 +326,11 @@ final class HazelcastSession implements Session {
311326

312327
private boolean isNew;
313328

314-
private boolean changed;
329+
private boolean sessionIdChanged;
330+
331+
private boolean lastAccessedTimeChanged;
332+
333+
private boolean maxInactiveIntervalChanged;
315334

316335
private String originalId;
317336

@@ -341,7 +360,7 @@ final class HazelcastSession implements Session {
341360
@Override
342361
public void setLastAccessedTime(Instant lastAccessedTime) {
343362
this.delegate.setLastAccessedTime(lastAccessedTime);
344-
this.changed = true;
363+
this.lastAccessedTimeChanged = true;
345364
flushImmediateIfNecessary();
346365
}
347366

@@ -362,8 +381,9 @@ public String getId() {
362381

363382
@Override
364383
public String changeSessionId() {
365-
this.isNew = true;
366-
return this.delegate.changeSessionId();
384+
String newSessionId = this.delegate.changeSessionId();
385+
this.sessionIdChanged = true;
386+
return newSessionId;
367387
}
368388

369389
@Override
@@ -374,7 +394,7 @@ public Instant getLastAccessedTime() {
374394
@Override
375395
public void setMaxInactiveInterval(Duration interval) {
376396
this.delegate.setMaxInactiveInterval(interval);
377-
this.changed = true;
397+
this.maxInactiveIntervalChanged = true;
378398
flushImmediateIfNecessary();
379399
}
380400

@@ -397,25 +417,30 @@ public Set<String> getAttributeNames() {
397417
public void setAttribute(String attributeName, Object attributeValue) {
398418
this.delegate.setAttribute(attributeName, attributeValue);
399419
this.delta.put(attributeName, attributeValue);
400-
this.changed = true;
401420
flushImmediateIfNecessary();
402421
}
403422

404423
@Override
405424
public void removeAttribute(String attributeName) {
406425
this.delegate.removeAttribute(attributeName);
407426
this.delta.put(attributeName, null);
408-
this.changed = true;
409427
flushImmediateIfNecessary();
410428
}
411429

412430
MapSession getDelegate() {
413431
return this.delegate;
414432
}
415433

416-
void clearFlags() {
434+
boolean hasChanges() {
435+
return (this.lastAccessedTimeChanged || this.maxInactiveIntervalChanged
436+
|| !this.delta.isEmpty());
437+
}
438+
439+
void clearChangeFlags() {
417440
this.isNew = false;
418-
this.changed = false;
441+
this.lastAccessedTimeChanged = false;
442+
this.sessionIdChanged = false;
443+
this.maxInactiveIntervalChanged = false;
419444
this.delta.clear();
420445
}
421446

@@ -436,39 +461,50 @@ private void flushImmediateIfNecessary() {
436461
private static final class SessionUpdateEntryProcessor
437462
extends AbstractEntryProcessor<String, MapSession> {
438463

439-
private final Instant lastAccessedTime;
464+
private Instant lastAccessedTime;
440465

441-
private final Duration maxInactiveInterval;
466+
private Duration maxInactiveInterval;
442467

443-
private final Map<String, Object> delta;
444-
445-
SessionUpdateEntryProcessor(Instant lastAccessedTime,
446-
Duration maxInactiveInterval, Map<String, Object> delta) {
447-
this.lastAccessedTime = lastAccessedTime;
448-
this.maxInactiveInterval = maxInactiveInterval;
449-
this.delta = delta;
450-
}
468+
private Map<String, Object> delta;
451469

452470
@Override
453471
public Object process(Map.Entry<String, MapSession> entry) {
454472
MapSession value = entry.getValue();
455473
if (value == null) {
456474
return Boolean.FALSE;
457475
}
458-
value.setLastAccessedTime(this.lastAccessedTime);
459-
value.setMaxInactiveInterval(this.maxInactiveInterval);
460-
for (final Map.Entry<String, Object> attribute : this.delta.entrySet()) {
461-
if (attribute.getValue() != null) {
462-
value.setAttribute(attribute.getKey(), attribute.getValue());
463-
}
464-
else {
465-
value.removeAttribute(attribute.getKey());
476+
if (this.lastAccessedTime != null) {
477+
value.setLastAccessedTime(this.lastAccessedTime);
478+
}
479+
if (this.maxInactiveInterval != null) {
480+
value.setMaxInactiveInterval(this.maxInactiveInterval);
481+
}
482+
if (this.delta != null) {
483+
for (final Map.Entry<String, Object> attribute : this.delta.entrySet()) {
484+
if (attribute.getValue() != null) {
485+
value.setAttribute(attribute.getKey(), attribute.getValue());
486+
}
487+
else {
488+
value.removeAttribute(attribute.getKey());
489+
}
466490
}
467491
}
468492
entry.setValue(value);
469493
return Boolean.TRUE;
470494
}
471495

496+
public void setLastAccessedTime(Instant lastAccessedTime) {
497+
this.lastAccessedTime = lastAccessedTime;
498+
}
499+
500+
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
501+
this.maxInactiveInterval = maxInactiveInterval;
502+
}
503+
504+
public void setDelta(Map<String, Object> delta) {
505+
this.delta = delta;
506+
}
507+
472508
}
473509

474510
}

0 commit comments

Comments
 (0)