Skip to content

Commit

Permalink
Introduce SessionIdGenerationStrategy
Browse files Browse the repository at this point in the history
Issue gh-11
  • Loading branch information
marcusdacoregio committed Apr 5, 2023
1 parent 3ead793 commit d154602
Show file tree
Hide file tree
Showing 25 changed files with 572 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@
*/
public class MapSessionRepository implements SessionRepository<MapSession> {

private static final SessionIdGenerationStrategy DEFAULT_STRATEGY = UuidSessionIdGenerationStrategy.getInstance();

private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);

private final Map<String, Session> sessions;

private SessionIdGenerationStrategy sessionIdGenerationStrategy = DEFAULT_STRATEGY;

/**
* Creates a new instance backed by the provided {@link java.util.Map}. This allows
* injecting a distributed {@link java.util.Map}.
Expand Down Expand Up @@ -94,9 +98,14 @@ public void deleteById(String id) {

@Override
public MapSession createSession() {
MapSession result = new MapSession();
MapSession result = new MapSession(this.sessionIdGenerationStrategy.generate());
result.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
return result;
}

public void setSessionIdGenerationStrategy(SessionIdGenerationStrategy sessionIdGenerationStrategy) {
Assert.notNull(sessionIdGenerationStrategy, "sessionIdGenerationStrategy cannot be null");
this.sessionIdGenerationStrategy = sessionIdGenerationStrategy;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@
*/
public class ReactiveMapSessionRepository implements ReactiveSessionRepository<MapSession> {

private static final SessionIdGenerationStrategy DEFAULT_STRATEGY = UuidSessionIdGenerationStrategy.getInstance();

private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);

private final Map<String, Session> sessions;

private SessionIdGenerationStrategy sessionIdGenerationStrategy = DEFAULT_STRATEGY;

/**
* Creates a new instance backed by the provided {@link Map}. This allows injecting a
* distributed {@link Map}.
Expand Down Expand Up @@ -96,10 +100,21 @@ public Mono<Void> deleteById(String id) {
@Override
public Mono<MapSession> createSession() {
return Mono.defer(() -> {
MapSession result = new MapSession();
MapSession result = new MapSession(this.sessionIdGenerationStrategy.generate());
result.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
return Mono.just(result);
});
}

/**
* Sets the {@link SessionIdGenerationStrategy} to use.
* @param sessionIdGenerationStrategy the non-null {@link SessionIdGenerationStrategy}
* to use
* @since 3.1
*/
public void setSessionIdGenerationStrategy(SessionIdGenerationStrategy sessionIdGenerationStrategy) {
Assert.notNull(sessionIdGenerationStrategy, "sessionIdGenerationStrategy cannot be null");
this.sessionIdGenerationStrategy = sessionIdGenerationStrategy;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.session;

import org.springframework.lang.NonNull;

public interface SessionIdGenerationStrategy {

@NonNull
String generate();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.session;

import java.util.UUID;

import org.springframework.lang.NonNull;

public final class UuidSessionIdGenerationStrategy implements SessionIdGenerationStrategy {

private static final UuidSessionIdGenerationStrategy INSTANCE = new UuidSessionIdGenerationStrategy();

private UuidSessionIdGenerationStrategy() {
}

@Override
@NonNull
public String generate() {
return UUID.randomUUID().toString();
}

public static UuidSessionIdGenerationStrategy getInstance() {
return INSTANCE;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import org.springframework.lang.Nullable;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.SessionIdGenerationStrategy;
import org.springframework.session.UuidSessionIdGenerationStrategy;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
Expand Down Expand Up @@ -70,6 +72,8 @@ public class MongoIndexedSessionRepository

private static final Log logger = LogFactory.getLog(MongoIndexedSessionRepository.class);

private static final SessionIdGenerationStrategy DEFAULT_STRATEGY = UuidSessionIdGenerationStrategy.getInstance();

private final MongoOperations mongoOperations;

private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
Expand All @@ -81,25 +85,36 @@ public class MongoIndexedSessionRepository

private ApplicationEventPublisher eventPublisher;

private SessionIdGenerationStrategy sessionIdGenerationStrategy = DEFAULT_STRATEGY;

public MongoIndexedSessionRepository(MongoOperations mongoOperations) {
this.mongoOperations = mongoOperations;
}

@Override
public MongoSession createSession() {

MongoSession session = new MongoSession();
String sessionId = this.sessionIdGenerationStrategy.generate();
MongoSession session = new MongoSession(sessionId);

session.setMaxInactiveInterval(this.defaultMaxInactiveInterval);

publishEvent(new SessionCreatedEvent(this, session));

return session;
return wrapSession(session);
}

private SessionIdGenerationStrategyAwareMongoSession wrapSession(MongoSession session) {
return new SessionIdGenerationStrategyAwareMongoSession(session, this.sessionIdGenerationStrategy);
}

@Override
public void save(MongoSession session) {
DBObject dbObject = MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, session);
MongoSession mongoSession = session;
if (session instanceof SessionIdGenerationStrategyAwareMongoSession awareMongoSession) {
mongoSession = awareMongoSession.getDelegate();
}
DBObject dbObject = MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, mongoSession);
Assert.notNull(dbObject, "dbObject must not be null");
this.mongoOperations.save(dbObject, this.collectionName);
}
Expand All @@ -116,10 +131,13 @@ public MongoSession findById(String id) {

MongoSession session = MongoSessionUtils.convertToSession(this.mongoSessionConverter, sessionWrapper);

if (session != null && session.isExpired()) {
publishEvent(new SessionExpiredEvent(this, session));
deleteById(id);
return null;
if (session != null) {
if (session.isExpired()) {
publishEvent(new SessionExpiredEvent(this, session));
deleteById(id);
return null;
}
return wrapSession(session);
}

return session;
Expand All @@ -140,7 +158,7 @@ public Map<String, MongoSession> findByIndexNameAndIndexValue(String indexName,
.map((query) -> this.mongoOperations.find(query, Document.class, this.collectionName))
.orElse(Collections.emptyList()).stream()
.map((dbSession) -> MongoSessionUtils.convertToSession(this.mongoSessionConverter, dbSession))
.collect(Collectors.toMap(MongoSession::getId, (mapSession) -> mapSession));
.map(this::wrapSession).collect(Collectors.toMap(MongoSession::getId, (mapSession) -> mapSession));
}

@Override
Expand Down Expand Up @@ -216,4 +234,9 @@ public void setMongoSessionConverter(final AbstractMongoSessionConverter mongoSe
this.mongoSessionConverter = mongoSessionConverter;
}

public void setSessionIdGenerationStrategy(SessionIdGenerationStrategy sessionIdGenerationStrategy) {
Assert.notNull(sessionIdGenerationStrategy, "sessionIdGenerationStrategy cannot be null");
this.sessionIdGenerationStrategy = sessionIdGenerationStrategy;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ class MongoSession implements Session {

private Map<String, Object> attrs = new HashMap<>();

/**
* Constructs a new instance using the provided session id.
* @param sessionId the session id to use
* @since 3.1
*/
MongoSession(String sessionId) {
this(sessionId, MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
}

MongoSession() {
this(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
}
Expand All @@ -79,7 +88,7 @@ class MongoSession implements Session {
this.id = id;
this.originalSessionId = id;
this.intervalSeconds = maxInactiveIntervalInSeconds;
setLastAccessedTime(Instant.ofEpochMilli(this.createdMillis));
setLastAccessedTimeAndUpdateExpireAt(Instant.ofEpochMilli(this.createdMillis));
}

static String coverDot(String attributeName) {
Expand Down Expand Up @@ -141,7 +150,10 @@ public Instant getLastAccessedTime() {

@Override
public void setLastAccessedTime(Instant lastAccessedTime) {
setLastAccessedTimeAndUpdateExpireAt(lastAccessedTime);
}

private void setLastAccessedTimeAndUpdateExpireAt(Instant lastAccessedTime) {
this.accessedMillis = lastAccessedTime.toEpochMilli();
this.expireAt = Date.from(lastAccessedTime.plus(Duration.ofSeconds(this.intervalSeconds)));
}
Expand Down Expand Up @@ -200,4 +212,13 @@ String getOriginalSessionId() {
return this.originalSessionId;
}

/**
* Sets the session id.
* @param id the id to set
* @since 3.1
*/
void setId(String id) {
this.id = id;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.session.MapSession;
import org.springframework.session.ReactiveSessionRepository;
import org.springframework.session.SessionIdGenerationStrategy;
import org.springframework.session.UuidSessionIdGenerationStrategy;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -63,6 +65,8 @@ public class ReactiveMongoSessionRepository

private static final Log logger = LogFactory.getLog(ReactiveMongoSessionRepository.class);

private static final SessionIdGenerationStrategy DEFAULT_STRATEGY = UuidSessionIdGenerationStrategy.getInstance();

private final ReactiveMongoOperations mongoOperations;

private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
Expand All @@ -76,6 +80,8 @@ public class ReactiveMongoSessionRepository

private ApplicationEventPublisher eventPublisher;

private SessionIdGenerationStrategy sessionIdGenerationStrategy = DEFAULT_STRATEGY;

public ReactiveMongoSessionRepository(ReactiveMongoOperations mongoOperations) {
this.mongoOperations = mongoOperations;
}
Expand All @@ -94,17 +100,20 @@ public ReactiveMongoSessionRepository(ReactiveMongoOperations mongoOperations) {
@Override
public Mono<MongoSession> createSession() {

return Mono.justOrEmpty(this.defaultMaxInactiveInterval.toSeconds()) //
.map(MongoSession::new) //
.doOnNext((mongoSession) -> publishEvent(new SessionCreatedEvent(this, mongoSession))) //
.switchIfEmpty(Mono.just(new MongoSession()));
return Mono.justOrEmpty(this.defaultMaxInactiveInterval.toSeconds())
.map((interval) -> new MongoSession(this.sessionIdGenerationStrategy.generate(), interval))
.doOnNext((mongoSession) -> publishEvent(new SessionCreatedEvent(this, mongoSession)))
.switchIfEmpty(Mono.just(new MongoSession(this.sessionIdGenerationStrategy.generate())));
}

@Override
public Mono<Void> save(MongoSession session) {

return Mono //
.justOrEmpty(MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, session)) //
return Mono.justOrEmpty(session)
.filter((mongoSession) -> mongoSession instanceof SessionIdGenerationStrategyAwareMongoSession)
.map((mongoSession) -> ((SessionIdGenerationStrategyAwareMongoSession) mongoSession).getDelegate())
.switchIfEmpty(Mono.justOrEmpty(session))
.map((mongoSession) -> MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, mongoSession))
.flatMap((dbObject) -> {
if (session.hasChangedSessionId()) {

Expand All @@ -127,7 +136,9 @@ public Mono<MongoSession> findById(String id) {
return findSession(id) //
.map((document) -> MongoSessionUtils.convertToSession(this.mongoSessionConverter, document)) //
.filter((mongoSession) -> !mongoSession.isExpired()) //
.switchIfEmpty(Mono.defer(() -> this.deleteById(id).then(Mono.empty())));
.map((mongoSession) -> new SessionIdGenerationStrategyAwareMongoSession(mongoSession,
this.sessionIdGenerationStrategy))
.cast(MongoSession.class).switchIfEmpty(Mono.defer(() -> this.deleteById(id).then(Mono.empty())));
}

@Override
Expand Down Expand Up @@ -216,4 +227,9 @@ public void setBlockingMongoOperations(final MongoOperations blockingMongoOperat
this.blockingMongoOperations = blockingMongoOperations;
}

public void setSessionIdGenerationStrategy(SessionIdGenerationStrategy sessionIdGenerationStrategy) {
Assert.notNull(sessionIdGenerationStrategy, "sessionIdGenerationStrategy cannot be null");
this.sessionIdGenerationStrategy = sessionIdGenerationStrategy;
}

}

0 comments on commit d154602

Please sign in to comment.