Introduce ReactiveMongoSessionRepository.
This commit is contained in:
@@ -15,183 +15,20 @@
|
||||
*/
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import static org.springframework.session.data.mongo.MongoSessionUtils.*;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* This {@link ReactiveSessionRepository} implementation is kept to support migration to
|
||||
* {@link ReactiveMongoSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @deprecated since 2.2.0 in favor of {@link ReactiveMongoSessionRepository}.
|
||||
*/
|
||||
public class ReactiveMongoOperationsSessionRepository
|
||||
implements ReactiveSessionRepository<MongoSession>, ApplicationEventPublisherAware, InitializingBean {
|
||||
|
||||
/**
|
||||
* The default time period in seconds in which a session will expire.
|
||||
*/
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
|
||||
|
||||
/**
|
||||
* The default collection name for storing session.
|
||||
*/
|
||||
public static final String DEFAULT_COLLECTION_NAME = "sessions";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ReactiveMongoOperationsSessionRepository.class);
|
||||
|
||||
private final ReactiveMongoOperations mongoOperations;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
|
||||
private String collectionName = DEFAULT_COLLECTION_NAME;
|
||||
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(
|
||||
Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
private MongoOperations blockingMongoOperations;
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
@Deprecated
|
||||
public class ReactiveMongoOperationsSessionRepository extends ReactiveMongoSessionRepository {
|
||||
|
||||
public ReactiveMongoOperationsSessionRepository(ReactiveMongoOperations mongoOperations) {
|
||||
this.mongoOperations = mongoOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoSession} that is capable of being persisted by this {@link ReactiveSessionRepository}.
|
||||
* <p>
|
||||
* This allows optimizations and customizations in how the {@link MongoSession} is persisted. For example, the
|
||||
* implementation returned might keep track of the changes ensuring that only the delta needs to be persisted on a
|
||||
* save.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@link MongoSession} that is capable of being persisted by this {@link ReactiveSessionRepository}
|
||||
*/
|
||||
@Override
|
||||
public Mono<MongoSession> createSession() {
|
||||
|
||||
return Mono.justOrEmpty(this.maxInactiveIntervalInSeconds) //
|
||||
.map(MongoSession::new) //
|
||||
.doOnNext(mongoSession -> publishEvent(new SessionCreatedEvent(this, mongoSession))) //
|
||||
.switchIfEmpty(Mono.just(new MongoSession()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the {@link MongoSession} created by {@link ReactiveSessionRepository#createSession()} is saved.
|
||||
* <p>
|
||||
* Some implementations may choose to save as the {@link MongoSession} is updated by returning a {@link MongoSession}
|
||||
* that immediately persists any changes. In this case, this method may not actually do anything.
|
||||
* </p>
|
||||
*
|
||||
* @param session the {@link MongoSession} to save
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> save(MongoSession session) {
|
||||
|
||||
DBObject dbObject = convertToDBObject(this.mongoSessionConverter, session);
|
||||
if (dbObject != null) {
|
||||
return this.mongoOperations.save(dbObject, this.collectionName).then();
|
||||
} else {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link MongoSession} by the {@link MongoSession#getId()} or {@link Mono#empty()} if no
|
||||
* {@link MongoSession} is found.
|
||||
*
|
||||
* @param id the {@link MongoSession#getId()} to lookup
|
||||
* @return the {@link MongoSession} by the {@link MongoSession#getId()} or {@link Mono#empty()} if no
|
||||
* {@link MongoSession} is found.
|
||||
*/
|
||||
@Override
|
||||
public Mono<MongoSession> findById(String id) {
|
||||
|
||||
return findSession(id) //
|
||||
.map(document -> convertToSession(this.mongoSessionConverter, document)) //
|
||||
.filter(mongoSession -> !mongoSession.isExpired()) //
|
||||
.switchIfEmpty(Mono.defer(() -> this.deleteById(id).then(Mono.empty())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the {@link MongoSession} with the given {@link MongoSession#getId()} or does nothing if the
|
||||
* {@link MongoSession} is not found.
|
||||
*
|
||||
* @param id the {@link MongoSession#getId()} to delete
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> deleteById(String id) {
|
||||
|
||||
return findSession(id) //
|
||||
.flatMap(document -> this.mongoOperations.remove(document, this.collectionName) //
|
||||
.then(Mono.just(document))) //
|
||||
.map(document -> convertToSession(this.mongoSessionConverter, document)) //
|
||||
.doOnNext(mongoSession -> publishEvent(new SessionDeletedEvent(this, mongoSession))) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use {@link org.springframework.data.mongodb.core.index.ReactiveIndexOperations} to ensure indexes exist.
|
||||
* Instead, get a blocking {@link IndexOperations} and use that instead, if possible.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
if (this.blockingMongoOperations != null) {
|
||||
IndexOperations indexOperations = this.blockingMongoOperations.indexOps(this.collectionName);
|
||||
this.mongoSessionConverter.ensureIndexes(indexOperations);
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Document> findSession(String id) {
|
||||
return this.mongoOperations.findById(id, Document.class, this.collectionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
|
||||
try {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
} catch (Throwable ex) {
|
||||
logger.error("Error publishing " + event + ".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getMaxInactiveIntervalInSeconds() {
|
||||
return this.maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(final Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return this.collectionName;
|
||||
}
|
||||
|
||||
public void setCollectionName(final String collectionName) {
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
public void setMongoSessionConverter(final AbstractMongoSessionConverter mongoSessionConverter) {
|
||||
this.mongoSessionConverter = mongoSessionConverter;
|
||||
}
|
||||
|
||||
public void setBlockingMongoOperations(final MongoOperations blockingMongoOperations) {
|
||||
this.blockingMongoOperations = blockingMongoOperations;
|
||||
super(mongoOperations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2019 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.data.mongo;
|
||||
|
||||
import static org.springframework.session.data.mongo.MongoSessionUtils.*;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} implementation that uses Spring Data MongoDB.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class ReactiveMongoSessionRepository
|
||||
implements ReactiveSessionRepository<MongoSession>, ApplicationEventPublisherAware, InitializingBean {
|
||||
|
||||
/**
|
||||
* The default time period in seconds in which a session will expire.
|
||||
*/
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
|
||||
|
||||
/**
|
||||
* The default collection name for storing session.
|
||||
*/
|
||||
public static final String DEFAULT_COLLECTION_NAME = "sessions";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ReactiveMongoSessionRepository.class);
|
||||
|
||||
private final ReactiveMongoOperations mongoOperations;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
|
||||
private String collectionName = DEFAULT_COLLECTION_NAME;
|
||||
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(
|
||||
Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
private MongoOperations blockingMongoOperations;
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public ReactiveMongoSessionRepository(ReactiveMongoOperations mongoOperations) {
|
||||
this.mongoOperations = mongoOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoSession} that is capable of being persisted by this {@link ReactiveSessionRepository}.
|
||||
* <p>
|
||||
* This allows optimizations and customizations in how the {@link MongoSession} is persisted. For example, the
|
||||
* implementation returned might keep track of the changes ensuring that only the delta needs to be persisted on a
|
||||
* save.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@link MongoSession} that is capable of being persisted by this {@link ReactiveSessionRepository}
|
||||
*/
|
||||
@Override
|
||||
public Mono<MongoSession> createSession() {
|
||||
|
||||
return Mono.justOrEmpty(this.maxInactiveIntervalInSeconds) //
|
||||
.map(MongoSession::new) //
|
||||
.doOnNext(mongoSession -> publishEvent(new SessionCreatedEvent(this, mongoSession))) //
|
||||
.switchIfEmpty(Mono.just(new MongoSession()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(MongoSession session) {
|
||||
|
||||
DBObject dbObject = convertToDBObject(this.mongoSessionConverter, session);
|
||||
if (dbObject != null) {
|
||||
return this.mongoOperations.save(dbObject, this.collectionName).then();
|
||||
} else {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MongoSession> findById(String id) {
|
||||
|
||||
return findSession(id) //
|
||||
.map(document -> convertToSession(this.mongoSessionConverter, document)) //
|
||||
.filter(mongoSession -> !mongoSession.isExpired()) //
|
||||
.switchIfEmpty(Mono.defer(() -> this.deleteById(id).then(Mono.empty())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteById(String id) {
|
||||
|
||||
return findSession(id) //
|
||||
.flatMap(document -> this.mongoOperations.remove(document, this.collectionName) //
|
||||
.then(Mono.just(document))) //
|
||||
.map(document -> convertToSession(this.mongoSessionConverter, document)) //
|
||||
.doOnNext(mongoSession -> publishEvent(new SessionDeletedEvent(this, mongoSession))) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use {@link org.springframework.data.mongodb.core.index.ReactiveIndexOperations} to ensure indexes exist.
|
||||
* Instead, get a blocking {@link IndexOperations} and use that instead, if possible.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
if (this.blockingMongoOperations != null) {
|
||||
IndexOperations indexOperations = this.blockingMongoOperations.indexOps(this.collectionName);
|
||||
this.mongoSessionConverter.ensureIndexes(indexOperations);
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Document> findSession(String id) {
|
||||
return this.mongoOperations.findById(id, Document.class, this.collectionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
|
||||
try {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
} catch (Throwable ex) {
|
||||
logger.error("Error publishing " + event + ".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getMaxInactiveIntervalInSeconds() {
|
||||
return this.maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(final Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return this.collectionName;
|
||||
}
|
||||
|
||||
public void setCollectionName(final String collectionName) {
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
public void setMongoSessionConverter(final AbstractMongoSessionConverter mongoSessionConverter) {
|
||||
this.mongoSessionConverter = mongoSessionConverter;
|
||||
}
|
||||
|
||||
public void setBlockingMongoOperations(final MongoOperations blockingMongoOperations) {
|
||||
this.blockingMongoOperations = blockingMongoOperations;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
|
||||
/**
|
||||
* Add this annotation to a {@code @Configuration} class to configure a MongoDB-based {@code WebSessionManager} for a
|
||||
@@ -59,12 +59,12 @@ public @interface EnableMongoWebSession {
|
||||
*
|
||||
* @return default max inactive interval in seconds
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
int maxInactiveIntervalInSeconds() default ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
|
||||
/**
|
||||
* The collection name to use.
|
||||
*
|
||||
* @return name of the collection to store session
|
||||
*/
|
||||
String collectionName() default ReactiveMongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME;
|
||||
String collectionName() default ReactiveMongoSessionRepository.DEFAULT_COLLECTION_NAME;
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* Configure a {@link ReactiveMongoOperationsSessionRepository} using a provided {@link ReactiveMongoOperations}.
|
||||
* Configure a {@link ReactiveMongoSessionRepository} using a provided {@link ReactiveMongoOperations}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavić
|
||||
@@ -55,17 +55,16 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
private ClassLoader classLoader;
|
||||
|
||||
@Bean
|
||||
public ReactiveMongoOperationsSessionRepository reactiveMongoOperationsSessionRepository(
|
||||
ReactiveMongoOperations operations) {
|
||||
public ReactiveMongoSessionRepository reactiveMongoSessionRepository(ReactiveMongoOperations operations) {
|
||||
|
||||
ReactiveMongoOperationsSessionRepository repository = new ReactiveMongoOperationsSessionRepository(operations);
|
||||
ReactiveMongoSessionRepository repository = new ReactiveMongoSessionRepository(operations);
|
||||
|
||||
if (this.mongoSessionConverter != null) {
|
||||
repository.setMongoSessionConverter(this.mongoSessionConverter);
|
||||
} else {
|
||||
JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(new SerializingConverter(),
|
||||
new DeserializingConverter(this.classLoader),
|
||||
Duration.ofSeconds(ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL));
|
||||
Duration.ofSeconds(ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL));
|
||||
repository.setMongoSessionConverter(mongoSessionConverter);
|
||||
}
|
||||
|
||||
@@ -98,7 +97,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
if (attributes != null) {
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
} else {
|
||||
this.maxInactiveIntervalInSeconds = ReactiveMongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
this.maxInactiveIntervalInSeconds = ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
}
|
||||
|
||||
String collectionNameValue = attributes != null ? attributes.getString("collectionName") : "";
|
||||
|
||||
Reference in New Issue
Block a user