Skip to content

Commit df3e4c5

Browse files
committed
Add support for customizing session repository before initialization
This commit adds support for customizing session repository implementations (both SessionRepository and ReactiveSessionRepository) before initialization by introducing SessionRepositoryCustomizer and ReactiveSessionRepositoryCustomizer strategies. Resolves: #1499
1 parent f746233 commit df3e4c5

File tree

10 files changed

+244
-0
lines changed

10 files changed

+244
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2014-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.session.config;
18+
19+
import org.springframework.session.ReactiveSessionRepository;
20+
21+
/**
22+
* Strategy that can be used to customize the {@link ReactiveSessionRepository} before it
23+
* is fully initialized, in particular to tune its configuration.
24+
*
25+
* @param <T> the {@link ReactiveSessionRepository} type
26+
* @author Vedran Pavic
27+
* @since 2.2.0
28+
*/
29+
@FunctionalInterface
30+
public interface ReactiveSessionRepositoryCustomizer<T extends ReactiveSessionRepository> {
31+
32+
/**
33+
* Customize the {@link ReactiveSessionRepository}.
34+
* @param sessionRepository the {@link ReactiveSessionRepository} to customize
35+
*/
36+
void customize(T sessionRepository);
37+
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2014-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.session.config;
18+
19+
import org.springframework.session.SessionRepository;
20+
21+
/**
22+
* Strategy that can be used to customize the {@link SessionRepository} before it is fully
23+
* initialized, in particular to tune its configuration.
24+
*
25+
* @param <T> the {@link SessionRepository} type
26+
* @author Vedran Pavic
27+
* @since 2.2.0
28+
*/
29+
@FunctionalInterface
30+
public interface SessionRepositoryCustomizer<T extends SessionRepository> {
31+
32+
/**
33+
* Customize the {@link SessionRepository}.
34+
* @param sessionRepository the {@link SessionRepository} to customize
35+
*/
36+
void customize(T sessionRepository);
37+
38+
}

spring-session-data-redis/src/main/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import java.util.Arrays;
2020
import java.util.Collections;
21+
import java.util.List;
2122
import java.util.Map;
2223
import java.util.concurrent.Executor;
24+
import java.util.stream.Collectors;
2325

2426
import org.apache.commons.logging.LogFactory;
2527

@@ -51,6 +53,7 @@
5153
import org.springframework.session.FlushMode;
5254
import org.springframework.session.MapSession;
5355
import org.springframework.session.SaveMode;
56+
import org.springframework.session.config.SessionRepositoryCustomizer;
5457
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
5558
import org.springframework.session.data.redis.RedisFlushMode;
5659
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
@@ -103,6 +106,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
103106

104107
private Executor redisSubscriptionExecutor;
105108

109+
private List<SessionRepositoryCustomizer<RedisOperationsSessionRepository>> sessionRepositoryCustomizers;
110+
106111
private ClassLoader classLoader;
107112

108113
private StringValueResolver embeddedValueResolver;
@@ -123,6 +128,8 @@ public RedisOperationsSessionRepository sessionRepository() {
123128
sessionRepository.setSaveMode(this.saveMode);
124129
int database = resolveDatabase();
125130
sessionRepository.setDatabase(database);
131+
this.sessionRepositoryCustomizers
132+
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
126133
return sessionRepository;
127134
}
128135

@@ -221,6 +228,12 @@ public void setRedisSubscriptionExecutor(Executor redisSubscriptionExecutor) {
221228
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
222229
}
223230

231+
@Autowired(required = false)
232+
public void setSessionRepositoryCustomizer(
233+
ObjectProvider<SessionRepositoryCustomizer<RedisOperationsSessionRepository>> sessionRepositoryCustomizers) {
234+
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
235+
}
236+
224237
@Override
225238
public void setBeanClassLoader(ClassLoader classLoader) {
226239
this.classLoader = classLoader;

spring-session-data-redis/src/main/java/org/springframework/session/data/redis/config/annotation/web/server/RedisWebSessionConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.session.data.redis.config.annotation.web.server;
1818

19+
import java.util.List;
1920
import java.util.Map;
21+
import java.util.stream.Collectors;
2022

2123
import org.springframework.beans.factory.BeanClassLoaderAware;
2224
import org.springframework.beans.factory.ObjectProvider;
@@ -36,6 +38,7 @@
3638
import org.springframework.data.redis.serializer.StringRedisSerializer;
3739
import org.springframework.session.MapSession;
3840
import org.springframework.session.SaveMode;
41+
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
3942
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
4043
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
4144
import org.springframework.session.data.redis.RedisFlushMode;
@@ -68,6 +71,8 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
6871

6972
private RedisSerializer<Object> defaultRedisSerializer;
7073

74+
private List<ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository>> sessionRepositoryCustomizers;
75+
7176
private ClassLoader classLoader;
7277

7378
private StringValueResolver embeddedValueResolver;
@@ -82,6 +87,8 @@ public ReactiveRedisOperationsSessionRepository sessionRepository() {
8287
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
8388
}
8489
sessionRepository.setSaveMode(this.saveMode);
90+
this.sessionRepositoryCustomizers
91+
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
8592
return sessionRepository;
8693
}
8794

@@ -120,6 +127,12 @@ public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerial
120127
this.defaultRedisSerializer = defaultRedisSerializer;
121128
}
122129

130+
@Autowired(required = false)
131+
public void setSessionRepositoryCustomizer(
132+
ObjectProvider<ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository>> sessionRepositoryCustomizers) {
133+
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
134+
}
135+
123136
@Override
124137
public void setBeanClassLoader(ClassLoader classLoader) {
125138
this.classLoader = classLoader;

spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfigurationTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929
import org.springframework.context.annotation.Configuration;
3030
import org.springframework.context.annotation.Primary;
3131
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
32+
import org.springframework.core.annotation.Order;
3233
import org.springframework.data.redis.connection.RedisConnection;
3334
import org.springframework.data.redis.connection.RedisConnectionFactory;
3435
import org.springframework.data.redis.core.RedisOperations;
3536
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
3637
import org.springframework.mock.env.MockEnvironment;
3738
import org.springframework.session.FlushMode;
3839
import org.springframework.session.SaveMode;
40+
import org.springframework.session.config.SessionRepositoryCustomizer;
3941
import org.springframework.session.data.redis.RedisFlushMode;
4042
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
4143
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
@@ -57,6 +59,8 @@
5759
@SuppressWarnings("deprecation")
5860
class RedisHttpSessionConfigurationTests {
5961

62+
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
63+
6064
private static final String CLEANUP_CRON_EXPRESSION = "0 0 * * * *";
6165

6266
private AnnotationConfigApplicationContext context;
@@ -238,6 +242,15 @@ void customRedisMessageListenerContainerConfig() {
238242
assertThat(beans).containsKeys("springSessionRedisMessageListenerContainer", "redisMessageListenerContainer");
239243
}
240244

245+
@Test
246+
void sessionRepositoryCustomizer() {
247+
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
248+
RedisOperationsSessionRepository sessionRepository = this.context
249+
.getBean(RedisOperationsSessionRepository.class);
250+
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
251+
MAX_INACTIVE_INTERVAL_IN_SECONDS);
252+
}
253+
241254
private void registerAndRefresh(Class<?>... annotatedClasses) {
242255
this.context.register(annotatedClasses);
243256
this.context.refresh();
@@ -416,4 +429,22 @@ public RedisMessageListenerContainer redisMessageListenerContainer() {
416429

417430
}
418431

432+
@EnableRedisHttpSession
433+
static class SessionRepositoryCustomizerConfiguration {
434+
435+
@Bean
436+
@Order(0)
437+
public SessionRepositoryCustomizer<RedisOperationsSessionRepository> sessionRepositoryCustomizerOne() {
438+
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
439+
}
440+
441+
@Bean
442+
@Order(1)
443+
public SessionRepositoryCustomizer<RedisOperationsSessionRepository> sessionRepositoryCustomizerTwo() {
444+
return (sessionRepository) -> sessionRepository
445+
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
446+
}
447+
448+
}
449+
419450
}

spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/server/RedisWebSessionConfigurationTests.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
import org.springframework.context.annotation.Bean;
2626
import org.springframework.context.annotation.Configuration;
2727
import org.springframework.context.annotation.Primary;
28+
import org.springframework.core.annotation.Order;
2829
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
2930
import org.springframework.data.redis.core.ReactiveRedisOperations;
3031
import org.springframework.data.redis.serializer.RedisSerializationContext;
3132
import org.springframework.data.redis.serializer.RedisSerializer;
3233
import org.springframework.session.SaveMode;
34+
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
3335
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
3436
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
3537
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
@@ -222,6 +224,15 @@ void customRedisSerializerConfig() {
222224
"serializer")).isEqualTo(redisSerializer);
223225
}
224226

227+
@Test
228+
void sessionRepositoryCustomizer() {
229+
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
230+
ReactiveRedisOperationsSessionRepository sessionRepository = this.context
231+
.getBean(ReactiveRedisOperationsSessionRepository.class);
232+
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
233+
MAX_INACTIVE_INTERVAL_IN_SECONDS);
234+
}
235+
225236
private void registerAndRefresh(Class<?>... annotatedClasses) {
226237
this.context.register(annotatedClasses);
227238
this.context.refresh();
@@ -348,4 +359,22 @@ public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
348359

349360
}
350361

362+
@EnableRedisWebSession
363+
static class SessionRepositoryCustomizerConfiguration {
364+
365+
@Bean
366+
@Order(0)
367+
public ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository> sessionRepositoryCustomizerOne() {
368+
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
369+
}
370+
371+
@Bean
372+
@Order(1)
373+
public ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository> sessionRepositoryCustomizerTwo() {
374+
return (sessionRepository) -> sessionRepository
375+
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
376+
}
377+
378+
}
379+
351380
}

spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfiguration.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.session.hazelcast.config.annotation.web.http;
1818

19+
import java.util.List;
1920
import java.util.Map;
21+
import java.util.stream.Collectors;
2022

2123
import com.hazelcast.core.HazelcastInstance;
2224

@@ -31,6 +33,7 @@
3133
import org.springframework.session.FlushMode;
3234
import org.springframework.session.MapSession;
3335
import org.springframework.session.SaveMode;
36+
import org.springframework.session.config.SessionRepositoryCustomizer;
3437
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
3538
import org.springframework.session.hazelcast.HazelcastFlushMode;
3639
import org.springframework.session.hazelcast.HazelcastSessionRepository;
@@ -63,6 +66,8 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
6366

6467
private ApplicationEventPublisher applicationEventPublisher;
6568

69+
private List<SessionRepositoryCustomizer<HazelcastSessionRepository>> sessionRepositoryCustomizers;
70+
6671
@Bean
6772
public HazelcastSessionRepository sessionRepository() {
6873
HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository(this.hazelcastInstance);
@@ -73,6 +78,8 @@ public HazelcastSessionRepository sessionRepository() {
7378
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
7479
sessionRepository.setFlushMode(this.flushMode);
7580
sessionRepository.setSaveMode(this.saveMode);
81+
this.sessionRepositoryCustomizers
82+
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
7683
return sessionRepository;
7784
}
7885

@@ -113,6 +120,12 @@ public void setApplicationEventPublisher(ApplicationEventPublisher applicationEv
113120
this.applicationEventPublisher = applicationEventPublisher;
114121
}
115122

123+
@Autowired(required = false)
124+
public void setSessionRepositoryCustomizer(
125+
ObjectProvider<SessionRepositoryCustomizer<HazelcastSessionRepository>> sessionRepositoryCustomizers) {
126+
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
127+
}
128+
116129
@Override
117130
@SuppressWarnings("deprecation")
118131
public void setImportMetadata(AnnotationMetadata importMetadata) {

spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/config/annotation/web/http/HazelcastHttpSessionConfigurationTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import org.springframework.context.annotation.Bean;
2727
import org.springframework.context.annotation.Configuration;
2828
import org.springframework.context.annotation.Primary;
29+
import org.springframework.core.annotation.Order;
2930
import org.springframework.session.FlushMode;
3031
import org.springframework.session.SaveMode;
32+
import org.springframework.session.config.SessionRepositoryCustomizer;
3133
import org.springframework.session.hazelcast.HazelcastFlushMode;
3234
import org.springframework.session.hazelcast.HazelcastSessionRepository;
3335
import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance;
@@ -222,6 +224,14 @@ void multipleHazelcastInstanceConfiguration() {
222224
.withMessageContaining("expected single matching bean but found 2");
223225
}
224226

227+
@Test
228+
void sessionRepositoryCustomizer() {
229+
registerAndRefresh(SessionRepositoryCustomizerConfiguration.class);
230+
HazelcastSessionRepository sessionRepository = this.context.getBean(HazelcastSessionRepository.class);
231+
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
232+
MAX_INACTIVE_INTERVAL_IN_SECONDS);
233+
}
234+
225235
private void registerAndRefresh(Class<?>... annotatedClasses) {
226236
this.context.register(annotatedClasses);
227237
this.context.refresh();
@@ -421,4 +431,22 @@ public HazelcastInstance secondaryHazelcastInstance() {
421431

422432
}
423433

434+
@EnableHazelcastHttpSession
435+
static class SessionRepositoryCustomizerConfiguration extends BaseConfiguration {
436+
437+
@Bean
438+
@Order(0)
439+
public SessionRepositoryCustomizer<HazelcastSessionRepository> sessionRepositoryCustomizerOne() {
440+
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
441+
}
442+
443+
@Bean
444+
@Order(1)
445+
public SessionRepositoryCustomizer<HazelcastSessionRepository> sessionRepositoryCustomizerTwo() {
446+
return (sessionRepository) -> sessionRepository
447+
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
448+
}
449+
450+
}
451+
424452
}

0 commit comments

Comments
 (0)