Skip to content

Commit 7e26897

Browse files
vpavicrwinch
authored andcommitted
Add support for configuring Redis session cleanup cron
1 parent 9ea1fb9 commit 7e26897

File tree

7 files changed

+115
-135
lines changed

7 files changed

+115
-135
lines changed

samples/boot/findbyusername/spring-session-sample-boot-findbyusername.gradle

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,3 @@ dependencies {
2020
integrationTestCompile seleniumDependencies
2121
integrationTestCompile "org.testcontainers:testcontainers"
2222
}
23-
24-
integrationTest {
25-
doFirst {
26-
systemProperties['spring.session.redis.namespace'] = project.name
27-
}
28-
}

spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.springframework.data.redis.serializer.StringRedisSerializer;
4040
import org.springframework.expression.Expression;
4141
import org.springframework.expression.spel.standard.SpelExpressionParser;
42-
import org.springframework.scheduling.annotation.Scheduled;
4342
import org.springframework.session.FindByIndexNameSessionRepository;
4443
import org.springframework.session.MapSession;
4544
import org.springframework.session.Session;
@@ -401,7 +400,6 @@ public void save(RedisSession session) {
401400
}
402401
}
403402

404-
@Scheduled(cron = "${spring.session.cleanup.cron.expression:0 * * * * *}")
405403
public void cleanupExpiredSessions() {
406404
this.expirationPolicy.cleanExpiredSessions();
407405
}

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2016 the original author or authors.
2+
* Copyright 2014-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
1717
package org.springframework.session.data.redis.config.annotation.web.http;
1818

1919
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
2021
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
2123
import java.lang.annotation.Target;
2224

2325
import org.springframework.context.annotation.Configuration;
@@ -31,32 +33,40 @@
3133
* Add this annotation to an {@code @Configuration} class to expose the
3234
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
3335
* Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory}
34-
* must be provided. For example: <pre>
35-
* <code>
36-
* {@literal @Configuration}
37-
* {@literal @EnableRedisHttpSession}
36+
* must be provided. For example:
37+
*
38+
* <pre class="code">
39+
* &#064;Configuration
40+
* &#064;EnableRedisHttpSession
3841
* public class RedisHttpSessionConfig {
3942
*
40-
* {@literal @Bean}
43+
* &#064;Bean
4144
* public LettuceConnectionFactory connectionFactory() {
4245
* return new LettuceConnectionFactory();
4346
* }
4447
*
4548
* }
46-
* </code> </pre>
49+
* </pre>
4750
*
4851
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} instead.
4952
*
5053
* @author Rob Winch
54+
* @author Vedran Pavic
5155
* @since 1.0
5256
* @see EnableSpringHttpSession
5357
*/
54-
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
55-
@Target({ java.lang.annotation.ElementType.TYPE })
58+
@Retention(RetentionPolicy.RUNTIME)
59+
@Target(ElementType.TYPE)
5660
@Documented
5761
@Import(RedisHttpSessionConfiguration.class)
5862
@Configuration
5963
public @interface EnableRedisHttpSession {
64+
65+
/**
66+
* The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
67+
* This should be a non-negative integer.
68+
* @return the seconds a session can be inactive before expiring
69+
*/
6070
int maxInactiveIntervalInSeconds() default 1800;
6171

6272
/**
@@ -93,4 +103,11 @@
93103
* @since 1.1
94104
*/
95105
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
106+
107+
/**
108+
* The cron expression for expired session cleanup job. By default runs every minute.
109+
* @return the session cleanup cron expression
110+
*/
111+
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
112+
96113
}

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

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.context.annotation.Bean;
3333
import org.springframework.context.annotation.Configuration;
3434
import org.springframework.context.annotation.ImportAware;
35-
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
3635
import org.springframework.core.annotation.AnnotationAttributes;
3736
import org.springframework.core.type.AnnotationMetadata;
3837
import org.springframework.data.redis.connection.RedisConnection;
@@ -43,6 +42,8 @@
4342
import org.springframework.data.redis.serializer.RedisSerializer;
4443
import org.springframework.data.redis.serializer.StringRedisSerializer;
4544
import org.springframework.scheduling.annotation.EnableScheduling;
45+
import org.springframework.scheduling.annotation.SchedulingConfigurer;
46+
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
4647
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
4748
import org.springframework.session.data.redis.RedisFlushMode;
4849
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
@@ -68,42 +69,46 @@
6869
@Configuration
6970
@EnableScheduling
7071
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
71-
implements EmbeddedValueResolverAware, ImportAware {
72+
implements EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer {
73+
74+
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
7275

7376
private Integer maxInactiveIntervalInSeconds = 1800;
7477

7578
private String redisNamespace = "";
7679

7780
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
7881

82+
private String cleanupCron = DEFAULT_CLEANUP_CRON;
83+
7984
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
8085

8186
private RedisConnectionFactory redisConnectionFactory;
8287

8388
private RedisSerializer<Object> defaultRedisSerializer;
8489

90+
private ApplicationEventPublisher applicationEventPublisher;
91+
8592
private Executor redisTaskExecutor;
8693

8794
private Executor redisSubscriptionExecutor;
8895

8996
private StringValueResolver embeddedValueResolver;
9097

9198
@Bean
92-
public RedisOperationsSessionRepository sessionRepository(
93-
ApplicationEventPublisher applicationEventPublisher) {
99+
public RedisOperationsSessionRepository sessionRepository() {
94100
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate(
95101
this.redisConnectionFactory, this.defaultRedisSerializer);
96102
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
97103
redisTemplate);
98-
sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
104+
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
99105
if (this.defaultRedisSerializer != null) {
100106
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
101107
}
102108
sessionRepository
103109
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
104-
String redisNamespace = getRedisNamespace();
105-
if (StringUtils.hasText(redisNamespace)) {
106-
sessionRepository.setRedisKeyNamespace(redisNamespace);
110+
if (StringUtils.hasText(this.redisNamespace)) {
111+
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
107112
}
108113
sessionRepository.setRedisFlushMode(this.redisFlushMode);
109114
return sessionRepository;
@@ -135,15 +140,6 @@ public InitializingBean enableRedisKeyspaceNotificationsInitializer() {
135140
this.redisConnectionFactory, this.configureRedisAction);
136141
}
137142

138-
/**
139-
* Property placeholder to process the @Scheduled annotation.
140-
* @return the {@link PropertySourcesPlaceholderConfigurer} to use
141-
*/
142-
@Bean
143-
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
144-
return new PropertySourcesPlaceholderConfigurer();
145-
}
146-
147143
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
148144
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
149145
}
@@ -157,6 +153,10 @@ public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
157153
this.redisFlushMode = redisFlushMode;
158154
}
159155

156+
public void setCleanupCron(String cleanupCron) {
157+
this.cleanupCron = cleanupCron;
158+
}
159+
160160
/**
161161
* Sets the action to perform for configuring Redis.
162162
*
@@ -187,6 +187,12 @@ public void setDefaultRedisSerializer(
187187
this.defaultRedisSerializer = defaultRedisSerializer;
188188
}
189189

190+
@Autowired
191+
public void setApplicationEventPublisher(
192+
ApplicationEventPublisher applicationEventPublisher) {
193+
this.applicationEventPublisher = applicationEventPublisher;
194+
}
195+
190196
@Autowired(required = false)
191197
@Qualifier("springSessionRedisTaskExecutor")
192198
public void setRedisTaskExecutor(Executor redisTaskExecutor) {
@@ -206,17 +212,27 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) {
206212

207213
@Override
208214
public void setImportMetadata(AnnotationMetadata importMetadata) {
209-
Map<String, Object> enableAttrMap = importMetadata
215+
Map<String, Object> attributeMap = importMetadata
210216
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
211-
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
212-
this.maxInactiveIntervalInSeconds = enableAttrs
217+
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
218+
this.maxInactiveIntervalInSeconds = attributes
213219
.getNumber("maxInactiveIntervalInSeconds");
214-
String redisNamespaceValue = enableAttrs.getString("redisNamespace");
220+
String redisNamespaceValue = attributes.getString("redisNamespace");
215221
if (StringUtils.hasText(redisNamespaceValue)) {
216222
this.redisNamespace = this.embeddedValueResolver
217223
.resolveStringValue(redisNamespaceValue);
218224
}
219-
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
225+
this.redisFlushMode = attributes.getEnum("redisFlushMode");
226+
String cleanupCron = attributes.getString("cleanupCron");
227+
if (StringUtils.hasText(cleanupCron)) {
228+
this.cleanupCron = cleanupCron;
229+
}
230+
}
231+
232+
@Override
233+
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
234+
taskRegistrar.addCronTask(() -> sessionRepository().cleanupExpiredSessions(),
235+
this.cleanupCron);
220236
}
221237

222238
private static RedisTemplate<Object, Object> createRedisTemplate(
@@ -233,13 +249,6 @@ private static RedisTemplate<Object, Object> createRedisTemplate(
233249
return redisTemplate;
234250
}
235251

236-
private String getRedisNamespace() {
237-
if (StringUtils.hasText(this.redisNamespace)) {
238-
return this.redisNamespace;
239-
}
240-
return System.getProperty("spring.session.redis.namespace", "");
241-
}
242-
243252
/**
244253
* Ensures that Redis is configured to send keyspace notifications. This is important
245254
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.

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

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

0 commit comments

Comments
 (0)