From 2afebe8e78f6e50f5521d9d83e0952670acf2e89 Mon Sep 17 00:00:00 2001 From: Greg Turnquist Date: Tue, 19 Sep 2017 11:59:02 -0500 Subject: [PATCH] Imperatively create TTL indexes --- .../MongoOperationsSessionRepository.java | 4 +- ...ctiveMongoOperationsSessionRepository.java | 28 ++++- .../data/mongo/SessionConverterProvider.java | 34 ------ .../ReactiveMongoWebSessionConfiguration.java | 8 ++ ...eMongoOperationsSessionRepositoryTest.java | 22 ++++ ...ctiveMongoWebSessionConfigurationTest.java | 100 ++++++++++++++---- 6 files changed, 136 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/org/springframework/session/data/mongo/SessionConverterProvider.java diff --git a/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java b/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java index 147be86..b5b4eef 100644 --- a/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java +++ b/src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java @@ -62,9 +62,7 @@ public class MongoOperationsSessionRepository private final MongoOperations mongoOperations; - private AbstractMongoSessionConverter mongoSessionConverter = - SessionConverterProvider.getDefaultMongoConverter(); - + private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(); private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL; private String collectionName = DEFAULT_COLLECTION_NAME; diff --git a/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java b/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java index e32949a..673b46e 100644 --- a/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java +++ b/src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java @@ -17,9 +17,13 @@ package org.springframework.session.data.mongo; import static org.springframework.session.data.mongo.MongoSessionUtils.*; +import javax.annotation.PostConstruct; + import org.bson.Document; import reactor.core.publisher.Mono; +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.ReactorSessionRepository; /** @@ -39,10 +43,12 @@ public class ReactiveMongoOperationsSessionRepository implements ReactorSessionR private final ReactiveMongoOperations mongoOperations; - private AbstractMongoSessionConverter mongoSessionConverter = SessionConverterProvider.getDefaultMongoConverter(); + private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(); private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL; private String collectionName = DEFAULT_COLLECTION_NAME; + private MongoOperations blockingMongoOperations; + public ReactiveMongoOperationsSessionRepository(ReactiveMongoOperations mongoOperations) { this.mongoOperations = mongoOperations; } @@ -114,6 +120,19 @@ public class ReactiveMongoOperationsSessionRepository implements ReactorSessionR return this.mongoOperations.remove(findSession(id), this.collectionName).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. + */ + @PostConstruct + public void ensureIndexesAreCreated() { + + if (this.blockingMongoOperations != null) { + IndexOperations indexOperations = this.blockingMongoOperations.indexOps(this.collectionName); + this.mongoSessionConverter.ensureIndexes(indexOperations); + } + } + private Mono findSession(String id) { return this.mongoOperations.findById(id, Document.class, this.collectionName); } @@ -130,4 +149,11 @@ public class ReactiveMongoOperationsSessionRepository implements ReactorSessionR this.collectionName = collectionName; } + public MongoOperations getBlockingMongoOperations() { + return this.blockingMongoOperations; + } + + public void setBlockingMongoOperations(MongoOperations blockingMongoOperations) { + this.blockingMongoOperations = blockingMongoOperations; + } } diff --git a/src/main/java/org/springframework/session/data/mongo/SessionConverterProvider.java b/src/main/java/org/springframework/session/data/mongo/SessionConverterProvider.java deleted file mode 100644 index 0cdf819..0000000 --- a/src/main/java/org/springframework/session/data/mongo/SessionConverterProvider.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2016 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 - * - * http://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; - -/** - * Provider choosing proper AbstractMongoSessionConverter. - * - * @author Jakub Kubrynski - */ -final class SessionConverterProvider { - - private static final String JACKSON_CLASS_NAME = "com.fasterxml.jackson.databind.ObjectMapper"; - - private SessionConverterProvider() { - } - - static AbstractMongoSessionConverter getDefaultMongoConverter() { - return new JdkMongoSessionConverter(); - } - -} diff --git a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java index 7798852..4220dc0 100644 --- a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java +++ b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportAware; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository; @@ -41,6 +42,9 @@ public class ReactiveMongoWebSessionConfiguration implements EmbeddedValueResolv private String collectionName; private StringValueResolver embeddedValueResolver; + @Autowired(required = false) + private MongoOperations mongoOperations; + @Bean public ReactiveMongoOperationsSessionRepository reactiveMongoOperationsSessionRepository(ReactiveMongoOperations operations) { @@ -57,6 +61,10 @@ public class ReactiveMongoWebSessionConfiguration implements EmbeddedValueResolv if (this.collectionName != null) { repository.setCollectionName(this.collectionName); } + + if (this.mongoOperations != null) { + repository.setBlockingMongoOperations(this.mongoOperations); + } return repository; } diff --git a/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java b/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java index 95a53c1..6dfe78e 100644 --- a/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java +++ b/src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java @@ -32,7 +32,9 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; +import org.springframework.data.mongodb.core.index.IndexOperations; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -56,6 +58,9 @@ public class ReactiveMongoOperationsSessionRepositoryTest { private ReactiveMongoOperationsSessionRepository repository; + @Mock + private MongoOperations blockingMongoOperations; + @Before public void setUp() throws Exception { @@ -190,4 +195,21 @@ public class ReactiveMongoOperationsSessionRepositoryTest { return true; }); } + + @Test + public void shouldInvokeMethodToCreateIndexesImperatively() { + + // given + IndexOperations indexOperations = mock(IndexOperations.class); + given(this.blockingMongoOperations.indexOps((String) any())).willReturn(indexOperations); + + this.repository.setBlockingMongoOperations(this.blockingMongoOperations); + + // when + this.repository.ensureIndexesAreCreated(); + + // then + verify(this.blockingMongoOperations, times(1)).indexOps((String) any()); + verify(this.converter, times(1)).ensureIndexes(indexOperations); + } } diff --git a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java index 01389fa..bb5e2cc 100644 --- a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java +++ b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java @@ -16,15 +16,21 @@ package org.springframework.session.data.mongo.config.annotation.web.reactive; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.BDDMockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; import java.lang.reflect.Field; +import java.util.Collections; +import org.junit.After; import org.junit.Test; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +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.EnableSpringWebSession; import org.springframework.session.ReactorSessionRepository; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; @@ -42,31 +48,41 @@ import org.springframework.web.server.session.WebSessionManager; */ public class ReactiveMongoWebSessionConfigurationTest { + private AnnotationConfigApplicationContext context; + + @After + public void tearDown() { + + if (this.context != null) { + this.context.close(); + } + } + @Test public void enableSpringWebSessionConfiguresThings() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(GoodConfig.class); - ctx.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.register(GoodConfig.class); + this.context.refresh(); - WebSessionManager webSessionManagerFoundByType = ctx.getBean(WebSessionManager.class); - Object webSessionManagerFoundByName = ctx.getBean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME); + WebSessionManager webSessionManagerFoundByType = this.context.getBean(WebSessionManager.class); + Object webSessionManagerFoundByName = this.context.getBean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME); assertThat(webSessionManagerFoundByType).isNotNull(); assertThat(webSessionManagerFoundByName).isNotNull(); assertThat(webSessionManagerFoundByType).isEqualTo(webSessionManagerFoundByName); - assertThat(ctx.getBean(ReactorSessionRepository.class)).isNotNull(); + assertThat(this.context.getBean(ReactorSessionRepository.class)).isNotNull(); } @Test public void missingReactorSessionRepositoryBreaksAppContext() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(BadConfig.class); + this.context = new AnnotationConfigApplicationContext(); + this.context.register(BadConfig.class); assertThatExceptionOfType(UnsatisfiedDependencyException.class) - .isThrownBy(ctx::refresh) + .isThrownBy(this.context::refresh) .withMessageContaining("Error creating bean with name 'webSessionManager'") .withMessageContaining("No qualifying bean of type '" + ReactiveMongoOperations.class.getCanonicalName()); } @@ -74,11 +90,11 @@ public class ReactiveMongoWebSessionConfigurationTest { @Test public void defaultSessionConverterShouldBeJdkWhenOnClasspath() throws IllegalAccessException { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(GoodConfig.class); - ctx.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.register(GoodConfig.class); + this.context.refresh(); - ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class); + ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class); AbstractMongoSessionConverter converter = findMongoSessionConverter(repository); @@ -90,11 +106,11 @@ public class ReactiveMongoWebSessionConfigurationTest { @Test public void overridingMongoSessionConverterWithBeanShouldWork() throws IllegalAccessException { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(OverrideSessionConverterConfig.class); - ctx.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.register(OverrideSessionConverterConfig.class); + this.context.refresh(); - ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class); + ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class); AbstractMongoSessionConverter converter = findMongoSessionConverter(repository); @@ -106,11 +122,11 @@ public class ReactiveMongoWebSessionConfigurationTest { @Test public void overridingIntervalAndCollectionNameThroughAnnotationShouldWork() throws IllegalAccessException { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(OverrideMongoParametersConfig.class); - ctx.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.register(OverrideMongoParametersConfig.class); + this.context.refresh(); - ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class); + ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class); Field inactiveField = ReflectionUtils.findField(ReactiveMongoOperationsSessionRepository.class, "maxInactiveIntervalInSeconds"); ReflectionUtils.makeAccessible(inactiveField); @@ -124,6 +140,21 @@ public class ReactiveMongoWebSessionConfigurationTest { assertThat(collectionName).isEqualTo("test-case"); } + @Test + public void reactiveAndBlockingMongoOperationsShouldEnsureIndexing() { + + this.context = new AnnotationConfigApplicationContext(); + this.context.register(ConfigWithReactiveAndImperativeMongoOperations.class); + this.context.refresh(); + + MongoOperations operations = this.context.getBean(MongoOperations.class); + IndexOperations indexOperations = this.context.getBean(IndexOperations.class); + + verify(operations, times(1)).indexOps((String) any()); + verify(indexOperations, times(1)).getIndexInfo(); + verify(indexOperations, times(1)).ensureIndex(any()); + } + /** * Reflectively extract the {@link AbstractMongoSessionConverter} from the {@link ReactiveMongoOperationsSessionRepository}. * This is to avoid expanding the surface area of the API. @@ -184,4 +215,29 @@ public class ReactiveMongoWebSessionConfigurationTest { return mock(ReactiveMongoOperations.class); } } + + @EnableMongoWebSession + static class ConfigWithReactiveAndImperativeMongoOperations { + + @Bean + ReactiveMongoOperations reactiveMongoOperations() { + return mock(ReactiveMongoOperations.class); + } + + @Bean + IndexOperations indexOperations() { + + IndexOperations indexOperations = mock(IndexOperations.class); + given(indexOperations.getIndexInfo()).willReturn(Collections.emptyList()); + return indexOperations; + } + + @Bean + MongoOperations mongoOperations(IndexOperations indexOperations) { + + MongoOperations mongoOperations = mock(MongoOperations.class); + given(mongoOperations.indexOps((String) any())).willReturn(indexOperations); + return mongoOperations; + } + } }