@@ -207,6 +207,7 @@ private class InMemoryWebSession implements WebSession {
207207
208208 private final AtomicReference <State > state = new AtomicReference <>(State .NEW );
209209
210+ private final Lock lock = new ReentrantLock ();
210211
211212 public InMemoryWebSession (Instant creationTime , Duration maxIdleTime ) {
212213 this .creationTime = creationTime ;
@@ -256,29 +257,6 @@ public boolean isStarted() {
256257 return this .state .get ().equals (State .STARTED ) || !getAttributes ().isEmpty ();
257258 }
258259
259- @ Override
260- public Mono <Void > changeSessionId () {
261- return Mono .<Void >defer (() -> {
262- String currentId = this .id .get ();
263- InMemoryWebSessionStore .this .sessions .remove (currentId );
264- String newId = String .valueOf (idGenerator .generateId ());
265- this .id .set (newId );
266- InMemoryWebSessionStore .this .sessions .put (this .id .get (), this );
267- return Mono .empty ();
268- })
269- .subscribeOn (Schedulers .boundedElastic ())
270- .publishOn (Schedulers .parallel ())
271- .then ();
272- }
273-
274- @ Override
275- public Mono <Void > invalidate () {
276- this .state .set (State .EXPIRED );
277- getAttributes ().clear ();
278- InMemoryWebSessionStore .this .sessions .remove (this .id .get ());
279- return Mono .empty ();
280- }
281-
282260 @ Override
283261 @ SuppressWarnings ("NullAway" ) // Dataflow analysis limitation
284262 public Mono <Void > save () {
@@ -292,11 +270,19 @@ public Mono<Void> save() {
292270
293271 if (isStarted ()) {
294272 // Save
295- InMemoryWebSessionStore .this .sessions .put (this .id .get (), this );
273+ if (InMemoryWebSessionStore .this .sessions .get (getId ()) == null ) {
274+ this .lock .lock ();
275+ try {
276+ InMemoryWebSessionStore .this .sessions .putIfAbsent (getId (), this );
277+ }
278+ finally {
279+ this .lock .unlock ();
280+ }
281+ }
296282
297283 // Unless it was invalidated
298284 if (this .state .get ().equals (State .EXPIRED )) {
299- InMemoryWebSessionStore .this .sessions .remove (this . id . get ());
285+ InMemoryWebSessionStore .this .sessions .remove (getId ());
300286 return Mono .error (new IllegalStateException ("Session was invalidated" ));
301287 }
302288 }
@@ -307,12 +293,41 @@ public Mono<Void> save() {
307293 private void checkMaxSessionsLimit () {
308294 if (sessions .size () >= maxSessions ) {
309295 expiredSessionChecker .removeExpiredSessions (clock .instant ());
310- if (sessions .size () >= maxSessions && !sessions .containsKey (this . id . get ())) {
296+ if (sessions .size () >= maxSessions && !sessions .containsKey (getId ())) {
311297 throw new IllegalStateException ("Max sessions limit reached: " + sessions .size ());
312298 }
313299 }
314300 }
315301
302+ @ Override
303+ public Mono <Void > changeSessionId () {
304+ return Mono .<Void >defer (() -> {
305+ this .lock .lock ();
306+ try {
307+ String oldId = getId ();
308+ String newId = String .valueOf (idGenerator .generateId ());
309+ InMemoryWebSessionStore .this .sessions .remove (oldId );
310+ InMemoryWebSessionStore .this .sessions .put (newId , this );
311+ this .id .set (newId );
312+ }
313+ finally {
314+ this .lock .unlock ();
315+ }
316+ return Mono .empty ();
317+ })
318+ .subscribeOn (Schedulers .boundedElastic ())
319+ .publishOn (Schedulers .parallel ())
320+ .then ();
321+ }
322+
323+ @ Override
324+ public Mono <Void > invalidate () {
325+ this .state .set (State .EXPIRED );
326+ getAttributes ().clear ();
327+ InMemoryWebSessionStore .this .sessions .remove (getId ());
328+ return Mono .empty ();
329+ }
330+
316331 @ Override
317332 public boolean isExpired () {
318333 return isExpired (clock .instant ());
0 commit comments