From 3d44ef0872583b04c8e73fe77a1387f4097efdc3 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Mon, 30 Oct 2017 10:08:20 +0100 Subject: [PATCH] Add support for configuring Spring Session cleanup cron This commit adds support for configuring cron expression used for expired session cleanup job in Redis and JDBC session stores. Closes gh-10818 --- .../session/JdbcSessionConfiguration.java | 1 + .../session/JdbcSessionProperties.java | 15 ++++++++++ .../session/RedisSessionConfiguration.java | 1 + .../session/RedisSessionProperties.java | 15 ++++++++++ .../SessionAutoConfigurationJdbcTests.java | 29 +++++++++++++++---- .../SessionAutoConfigurationRedisTests.java | 18 ++++++++---- .../appendix-application-properties.adoc | 2 ++ 7 files changed, 71 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java index e71503d722..00d81db753 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java @@ -68,6 +68,7 @@ class JdbcSessionConfiguration { setMaxInactiveIntervalInSeconds(timeout); } setTableName(jdbcSessionProperties.getTableName()); + setCleanupCron(jdbcSessionProperties.getCleanupCron()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionProperties.java index 51cda062d5..11fc18d11d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionProperties.java @@ -33,6 +33,8 @@ public class JdbcSessionProperties { private static final String DEFAULT_TABLE_NAME = "SPRING_SESSION"; + private static final String DEFAULT_CLEANUP_CRON = "0 * * * * *"; + /** * Path to the SQL file to use to initialize the database schema. */ @@ -43,6 +45,11 @@ public class JdbcSessionProperties { */ private String tableName = DEFAULT_TABLE_NAME; + /** + * Cron expression for expired session cleanup job. + */ + private String cleanupCron = DEFAULT_CLEANUP_CRON; + /** * Database schema initialization mode. */ @@ -64,6 +71,14 @@ public class JdbcSessionProperties { this.tableName = tableName; } + public String getCleanupCron() { + return this.cleanupCron; + } + + public void setCleanupCron(String cleanupCron) { + this.cleanupCron = cleanupCron; + } + public DataSourceInitializationMode getInitializeSchema() { return this.initializeSchema; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionConfiguration.java index 2ca21fa073..e34383a805 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionConfiguration.java @@ -62,6 +62,7 @@ class RedisSessionConfiguration { } setRedisNamespace(redisSessionProperties.getNamespace()); setRedisFlushMode(redisSessionProperties.getFlushMode()); + setCleanupCron(redisSessionProperties.getCleanupCron()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionProperties.java index 3d87e1cb6d..8aede5002e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisSessionProperties.java @@ -28,6 +28,8 @@ import org.springframework.session.data.redis.RedisFlushMode; @ConfigurationProperties(prefix = "spring.session.redis") public class RedisSessionProperties { + private static final String DEFAULT_CLEANUP_CRON = "0 * * * * *"; + /** * Namespace for keys used to store sessions. */ @@ -38,6 +40,11 @@ public class RedisSessionProperties { */ private RedisFlushMode flushMode = RedisFlushMode.ON_SAVE; + /** + * Cron expression for expired session cleanup job. + */ + private String cleanupCron = DEFAULT_CLEANUP_CRON; + public String getNamespace() { return this.namespace; } @@ -54,4 +61,12 @@ public class RedisSessionProperties { this.flushMode = flushMode; } + public String getCleanupCron() { + return this.cleanupCron; + } + + public void setCleanupCron(String cleanupCron) { + this.cleanupCron = cleanupCron; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java index c177416548..7a66c2dfc4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java @@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; +import org.springframework.boot.autoconfigure.session.JdbcSessionConfiguration.SpringBootJdbcHttpSessionConfiguration; import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.test.context.HideClassesClassLoader; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; @@ -82,7 +83,10 @@ public class SessionAutoConfigurationJdbcTests .isEqualTo(DataSourceInitializationMode.EMBEDDED); assertThat(context.getBean(JdbcOperations.class) .queryForList("select * from SPRING_SESSION")).isEmpty(); - + SpringBootJdbcHttpSessionConfiguration configuration = context + .getBean(SpringBootJdbcHttpSessionConfiguration.class); + assertThat(new DirectFieldAccessor(configuration).getPropertyValue("cleanupCron")) + .isEqualTo("0 * * * * *"); } @Test @@ -114,10 +118,9 @@ public class SessionAutoConfigurationJdbcTests @Test public void customTableName() { - this.contextRunner - .withPropertyValues("spring.session.store-type=jdbc", - "spring.session.jdbc.table-name=FOO_BAR", - "spring.session.jdbc.schema=classpath:session/custom-schema-h2.sql") + this.contextRunner.withPropertyValues("spring.session.store-type=jdbc", + "spring.session.jdbc.table-name=FOO_BAR", + "spring.session.jdbc.schema=classpath:session/custom-schema-h2.sql") .run((context) -> { JdbcOperationsSessionRepository repository = validateSessionRepository( context, JdbcOperationsSessionRepository.class); @@ -131,4 +134,20 @@ public class SessionAutoConfigurationJdbcTests }); } + @Test + public void customCleanupCron() { + this.contextRunner + .withPropertyValues("spring.session.store-type=jdbc", + "spring.session.jdbc.cleanup-cron=0 0 12 * * *") + .run((context) -> { + assertThat( + context.getBean(JdbcSessionProperties.class).getCleanupCron()) + .isEqualTo("0 0 12 * * *"); + SpringBootJdbcHttpSessionConfiguration configuration = context + .getBean(SpringBootJdbcHttpSessionConfiguration.class); + assertThat(new DirectFieldAccessor(configuration) + .getPropertyValue("cleanupCron")).isEqualTo("0 0 12 * * *"); + }); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java index bca2455409..0c24ae097e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java @@ -22,6 +22,7 @@ import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.session.RedisSessionConfiguration.SpringBootRedisHttpSessionConfiguration; import org.springframework.boot.test.context.HideClassesClassLoader; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; @@ -39,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Redis specific tests for {@link SessionAutoConfiguration}. * * @author Stephane Nicoll + * @author Vedran Pavic */ public class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfigurationTests { @@ -54,7 +56,7 @@ public class SessionAutoConfigurationRedisTests this.contextRunner.withPropertyValues("spring.session.store-type=redis") .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .run(validateSpringSessionUsesRedis("spring:session:event:created:", - RedisFlushMode.ON_SAVE)); + RedisFlushMode.ON_SAVE, "0 * * * * *")); } @Test @@ -66,7 +68,7 @@ public class SessionAutoConfigurationRedisTests MongoOperationsSessionRepository.class)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .run(validateSpringSessionUsesRedis("spring:session:event:created:", - RedisFlushMode.ON_SAVE)); + RedisFlushMode.ON_SAVE, "0 * * * * *")); } @Test @@ -75,13 +77,15 @@ public class SessionAutoConfigurationRedisTests .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo", - "spring.session.redis.flush-mode=immediate") + "spring.session.redis.flush-mode=immediate", + "spring.session.redis.cleanup-cron=0 0 12 * * *") .run(validateSpringSessionUsesRedis("spring:session:foo:event:created:", - RedisFlushMode.IMMEDIATE)); + RedisFlushMode.IMMEDIATE, "0 0 12 * * *")); } private ContextConsumer validateSpringSessionUsesRedis( - String sessionCreatedChannelPrefix, RedisFlushMode flushMode) { + String sessionCreatedChannelPrefix, RedisFlushMode flushMode, + String cleanupCron) { return (context) -> { RedisOperationsSessionRepository repository = validateSessionRepository( context, RedisOperationsSessionRepository.class); @@ -89,6 +93,10 @@ public class SessionAutoConfigurationRedisTests .isEqualTo(sessionCreatedChannelPrefix); assertThat(new DirectFieldAccessor(repository) .getPropertyValue("redisFlushMode")).isEqualTo(flushMode); + SpringBootRedisHttpSessionConfiguration configuration = context + .getBean(SpringBootRedisHttpSessionConfiguration.class); + assertThat(new DirectFieldAccessor(configuration) + .getPropertyValue("cleanupCron")).isEqualTo(cleanupCron); }; } diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 9dbea83a3b..6ad5d6aead 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -425,6 +425,7 @@ content into your application; rather pick only the properties that you need. spring.session.hazelcast.map-name=spring:session:sessions # Name of the map used to store sessions. # SPRING SESSION JDBC ({sc-spring-boot-autoconfigure}/session/JdbcSessionProperties.{sc-ext}[JdbcSessionProperties]) + spring.session.jdbc.cleanup-cron=0 * * * * * # Cron expression for expired session cleanup job. spring.session.jdbc.initialize-schema=embedded # Database schema initialization mode. spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. spring.session.jdbc.table-name=SPRING_SESSION # Name of database table used to store sessions. @@ -433,6 +434,7 @@ content into your application; rather pick only the properties that you need. spring.session.mongodb.collection-name=sessions # Collection name used to store sessions. # SPRING SESSION REDIS ({sc-spring-boot-autoconfigure}/session/RedisSessionProperties.{sc-ext}[RedisSessionProperties]) + spring.session.redis.cleanup-cron=0 * * * * * # Cron expression for expired session cleanup job. spring.session.redis.flush-mode=on-save # Sessions flush mode. spring.session.redis.namespace= # Namespace for keys used to store sessions.