Skip to content

Commit 2afebe8

Browse files
committed
Imperatively create TTL indexes
1 parent f4e3ed8 commit 2afebe8

File tree

6 files changed

+136
-60
lines changed

6 files changed

+136
-60
lines changed

src/main/java/org/springframework/session/data/mongo/MongoOperationsSessionRepository.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ public class MongoOperationsSessionRepository
6262

6363
private final MongoOperations mongoOperations;
6464

65-
private AbstractMongoSessionConverter mongoSessionConverter =
66-
SessionConverterProvider.getDefaultMongoConverter();
67-
65+
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter();
6866
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
6967
private String collectionName = DEFAULT_COLLECTION_NAME;
7068

src/main/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepository.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717

1818
import static org.springframework.session.data.mongo.MongoSessionUtils.*;
1919

20+
import javax.annotation.PostConstruct;
21+
2022
import org.bson.Document;
2123
import reactor.core.publisher.Mono;
24+
import org.springframework.data.mongodb.core.MongoOperations;
2225
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
26+
import org.springframework.data.mongodb.core.index.IndexOperations;
2327
import org.springframework.session.ReactorSessionRepository;
2428

2529
/**
@@ -39,10 +43,12 @@ public class ReactiveMongoOperationsSessionRepository implements ReactorSessionR
3943

4044
private final ReactiveMongoOperations mongoOperations;
4145

42-
private AbstractMongoSessionConverter mongoSessionConverter = SessionConverterProvider.getDefaultMongoConverter();
46+
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter();
4347
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
4448
private String collectionName = DEFAULT_COLLECTION_NAME;
4549

50+
private MongoOperations blockingMongoOperations;
51+
4652
public ReactiveMongoOperationsSessionRepository(ReactiveMongoOperations mongoOperations) {
4753
this.mongoOperations = mongoOperations;
4854
}
@@ -114,6 +120,19 @@ public Mono<Void> deleteById(String id) {
114120
return this.mongoOperations.remove(findSession(id), this.collectionName).then();
115121
}
116122

123+
/**
124+
* Do not use {@link org.springframework.data.mongodb.core.index.ReactiveIndexOperations} to ensure indexes exist.
125+
* Instead, get a blocking {@link IndexOperations} and use that instead, if possible.
126+
*/
127+
@PostConstruct
128+
public void ensureIndexesAreCreated() {
129+
130+
if (this.blockingMongoOperations != null) {
131+
IndexOperations indexOperations = this.blockingMongoOperations.indexOps(this.collectionName);
132+
this.mongoSessionConverter.ensureIndexes(indexOperations);
133+
}
134+
}
135+
117136
private Mono<Document> findSession(String id) {
118137
return this.mongoOperations.findById(id, Document.class, this.collectionName);
119138
}
@@ -130,4 +149,11 @@ public void setCollectionName(String collectionName) {
130149
this.collectionName = collectionName;
131150
}
132151

152+
public MongoOperations getBlockingMongoOperations() {
153+
return this.blockingMongoOperations;
154+
}
155+
156+
public void setBlockingMongoOperations(MongoOperations blockingMongoOperations) {
157+
this.blockingMongoOperations = blockingMongoOperations;
158+
}
133159
}

src/main/java/org/springframework/session/data/mongo/SessionConverterProvider.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.context.annotation.ImportAware;
2323
import org.springframework.core.annotation.AnnotationAttributes;
2424
import org.springframework.core.type.AnnotationMetadata;
25+
import org.springframework.data.mongodb.core.MongoOperations;
2526
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
2627
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
2728
import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository;
@@ -41,6 +42,9 @@ public class ReactiveMongoWebSessionConfiguration implements EmbeddedValueResolv
4142
private String collectionName;
4243
private StringValueResolver embeddedValueResolver;
4344

45+
@Autowired(required = false)
46+
private MongoOperations mongoOperations;
47+
4448
@Bean
4549
public ReactiveMongoOperationsSessionRepository reactiveMongoOperationsSessionRepository(ReactiveMongoOperations operations) {
4650

@@ -57,6 +61,10 @@ public ReactiveMongoOperationsSessionRepository reactiveMongoOperationsSessionRe
5761
if (this.collectionName != null) {
5862
repository.setCollectionName(this.collectionName);
5963
}
64+
65+
if (this.mongoOperations != null) {
66+
repository.setBlockingMongoOperations(this.mongoOperations);
67+
}
6068

6169
return repository;
6270
}

src/test/java/org/springframework/session/data/mongo/ReactiveMongoOperationsSessionRepositoryTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import reactor.test.StepVerifier;
3333

3434
import org.springframework.core.convert.TypeDescriptor;
35+
import org.springframework.data.mongodb.core.MongoOperations;
3536
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
37+
import org.springframework.data.mongodb.core.index.IndexOperations;
3638

3739
import com.mongodb.BasicDBObject;
3840
import com.mongodb.DBObject;
@@ -56,6 +58,9 @@ public class ReactiveMongoOperationsSessionRepositoryTest {
5658

5759
private ReactiveMongoOperationsSessionRepository repository;
5860

61+
@Mock
62+
private MongoOperations blockingMongoOperations;
63+
5964
@Before
6065
public void setUp() throws Exception {
6166

@@ -190,4 +195,21 @@ public void shouldDeleteSession() throws Exception {
190195
return true;
191196
});
192197
}
198+
199+
@Test
200+
public void shouldInvokeMethodToCreateIndexesImperatively() {
201+
202+
// given
203+
IndexOperations indexOperations = mock(IndexOperations.class);
204+
given(this.blockingMongoOperations.indexOps((String) any())).willReturn(indexOperations);
205+
206+
this.repository.setBlockingMongoOperations(this.blockingMongoOperations);
207+
208+
// when
209+
this.repository.ensureIndexesAreCreated();
210+
211+
// then
212+
verify(this.blockingMongoOperations, times(1)).indexOps((String) any());
213+
verify(this.converter, times(1)).ensureIndexes(indexOperations);
214+
}
193215
}

src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@
1616
package org.springframework.session.data.mongo.config.annotation.web.reactive;
1717

1818
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.Mockito.*;
19+
import static org.mockito.BDDMockito.*;
20+
import static org.mockito.Mockito.any;
21+
import static org.mockito.Mockito.mock;
2022

2123
import java.lang.reflect.Field;
24+
import java.util.Collections;
2225

26+
import org.junit.After;
2327
import org.junit.Test;
2428
import org.springframework.beans.factory.UnsatisfiedDependencyException;
2529
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2630
import org.springframework.context.annotation.Bean;
31+
import org.springframework.data.mongodb.core.MongoOperations;
2732
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
33+
import org.springframework.data.mongodb.core.index.IndexOperations;
2834
import org.springframework.session.EnableSpringWebSession;
2935
import org.springframework.session.ReactorSessionRepository;
3036
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
@@ -42,43 +48,53 @@
4248
*/
4349
public class ReactiveMongoWebSessionConfigurationTest {
4450

51+
private AnnotationConfigApplicationContext context;
52+
53+
@After
54+
public void tearDown() {
55+
56+
if (this.context != null) {
57+
this.context.close();
58+
}
59+
}
60+
4561
@Test
4662
public void enableSpringWebSessionConfiguresThings() {
4763

48-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
49-
ctx.register(GoodConfig.class);
50-
ctx.refresh();
64+
this.context = new AnnotationConfigApplicationContext();
65+
this.context.register(GoodConfig.class);
66+
this.context.refresh();
5167

52-
WebSessionManager webSessionManagerFoundByType = ctx.getBean(WebSessionManager.class);
53-
Object webSessionManagerFoundByName = ctx.getBean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME);
68+
WebSessionManager webSessionManagerFoundByType = this.context.getBean(WebSessionManager.class);
69+
Object webSessionManagerFoundByName = this.context.getBean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME);
5470

5571
assertThat(webSessionManagerFoundByType).isNotNull();
5672
assertThat(webSessionManagerFoundByName).isNotNull();
5773
assertThat(webSessionManagerFoundByType).isEqualTo(webSessionManagerFoundByName);
5874

59-
assertThat(ctx.getBean(ReactorSessionRepository.class)).isNotNull();
75+
assertThat(this.context.getBean(ReactorSessionRepository.class)).isNotNull();
6076
}
6177

6278
@Test
6379
public void missingReactorSessionRepositoryBreaksAppContext() {
6480

65-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
66-
ctx.register(BadConfig.class);
81+
this.context = new AnnotationConfigApplicationContext();
82+
this.context.register(BadConfig.class);
6783

6884
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
69-
.isThrownBy(ctx::refresh)
85+
.isThrownBy(this.context::refresh)
7086
.withMessageContaining("Error creating bean with name 'webSessionManager'")
7187
.withMessageContaining("No qualifying bean of type '" + ReactiveMongoOperations.class.getCanonicalName());
7288
}
7389

7490
@Test
7591
public void defaultSessionConverterShouldBeJdkWhenOnClasspath() throws IllegalAccessException {
7692

77-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
78-
ctx.register(GoodConfig.class);
79-
ctx.refresh();
93+
this.context = new AnnotationConfigApplicationContext();
94+
this.context.register(GoodConfig.class);
95+
this.context.refresh();
8096

81-
ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class);
97+
ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class);
8298

8399
AbstractMongoSessionConverter converter = findMongoSessionConverter(repository);
84100

@@ -90,11 +106,11 @@ public void defaultSessionConverterShouldBeJdkWhenOnClasspath() throws IllegalAc
90106
@Test
91107
public void overridingMongoSessionConverterWithBeanShouldWork() throws IllegalAccessException {
92108

93-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
94-
ctx.register(OverrideSessionConverterConfig.class);
95-
ctx.refresh();
109+
this.context = new AnnotationConfigApplicationContext();
110+
this.context.register(OverrideSessionConverterConfig.class);
111+
this.context.refresh();
96112

97-
ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class);
113+
ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class);
98114

99115
AbstractMongoSessionConverter converter = findMongoSessionConverter(repository);
100116

@@ -106,11 +122,11 @@ public void overridingMongoSessionConverterWithBeanShouldWork() throws IllegalAc
106122
@Test
107123
public void overridingIntervalAndCollectionNameThroughAnnotationShouldWork() throws IllegalAccessException {
108124

109-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
110-
ctx.register(OverrideMongoParametersConfig.class);
111-
ctx.refresh();
125+
this.context = new AnnotationConfigApplicationContext();
126+
this.context.register(OverrideMongoParametersConfig.class);
127+
this.context.refresh();
112128

113-
ReactiveMongoOperationsSessionRepository repository = ctx.getBean(ReactiveMongoOperationsSessionRepository.class);
129+
ReactiveMongoOperationsSessionRepository repository = this.context.getBean(ReactiveMongoOperationsSessionRepository.class);
114130

115131
Field inactiveField = ReflectionUtils.findField(ReactiveMongoOperationsSessionRepository.class, "maxInactiveIntervalInSeconds");
116132
ReflectionUtils.makeAccessible(inactiveField);
@@ -124,6 +140,21 @@ public void overridingIntervalAndCollectionNameThroughAnnotationShouldWork() thr
124140
assertThat(collectionName).isEqualTo("test-case");
125141
}
126142

143+
@Test
144+
public void reactiveAndBlockingMongoOperationsShouldEnsureIndexing() {
145+
146+
this.context = new AnnotationConfigApplicationContext();
147+
this.context.register(ConfigWithReactiveAndImperativeMongoOperations.class);
148+
this.context.refresh();
149+
150+
MongoOperations operations = this.context.getBean(MongoOperations.class);
151+
IndexOperations indexOperations = this.context.getBean(IndexOperations.class);
152+
153+
verify(operations, times(1)).indexOps((String) any());
154+
verify(indexOperations, times(1)).getIndexInfo();
155+
verify(indexOperations, times(1)).ensureIndex(any());
156+
}
157+
127158
/**
128159
* Reflectively extract the {@link AbstractMongoSessionConverter} from the {@link ReactiveMongoOperationsSessionRepository}.
129160
* This is to avoid expanding the surface area of the API.
@@ -184,4 +215,29 @@ ReactiveMongoOperations operations() {
184215
return mock(ReactiveMongoOperations.class);
185216
}
186217
}
218+
219+
@EnableMongoWebSession
220+
static class ConfigWithReactiveAndImperativeMongoOperations {
221+
222+
@Bean
223+
ReactiveMongoOperations reactiveMongoOperations() {
224+
return mock(ReactiveMongoOperations.class);
225+
}
226+
227+
@Bean
228+
IndexOperations indexOperations() {
229+
230+
IndexOperations indexOperations = mock(IndexOperations.class);
231+
given(indexOperations.getIndexInfo()).willReturn(Collections.emptyList());
232+
return indexOperations;
233+
}
234+
235+
@Bean
236+
MongoOperations mongoOperations(IndexOperations indexOperations) {
237+
238+
MongoOperations mongoOperations = mock(MongoOperations.class);
239+
given(mongoOperations.indexOps((String) any())).willReturn(indexOperations);
240+
return mongoOperations;
241+
}
242+
}
187243
}

0 commit comments

Comments
 (0)