From e07379244827b4c5129db20d34069ccd8bc06810 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Mon, 15 Jul 2019 06:10:50 +0200 Subject: [PATCH] Add support for configuring Spring Session SaveMode See gh-17514 --- .../session/HazelcastSessionConfiguration.java | 1 + .../session/HazelcastSessionProperties.java | 15 +++++++++++++++ .../session/JdbcSessionConfiguration.java | 1 + .../session/JdbcSessionProperties.java | 17 ++++++++++++++++- .../RedisReactiveSessionConfiguration.java | 1 + .../session/RedisSessionConfiguration.java | 1 + .../session/RedisSessionProperties.java | 15 +++++++++++++++ ...ctiveSessionAutoConfigurationRedisTests.java | 14 +++++++++----- .../SessionAutoConfigurationHazelcastTests.java | 11 +++++++++++ .../SessionAutoConfigurationJdbcTests.java | 14 ++++++++++++++ .../SessionAutoConfigurationRedisTests.java | 16 ++++++++++------ 11 files changed, 94 insertions(+), 12 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionConfiguration.java index 42dde47bf7..4b28512436 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionConfiguration.java @@ -59,6 +59,7 @@ class HazelcastSessionConfiguration { } setSessionMapName(hazelcastSessionProperties.getMapName()); setFlushMode(hazelcastSessionProperties.getFlushMode()); + setSaveMode(hazelcastSessionProperties.getSaveMode()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionProperties.java index 2d1bab9820..bab82e9a87 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/HazelcastSessionProperties.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.session; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.session.FlushMode; +import org.springframework.session.SaveMode; /** * Configuration properties for Hazelcast backed Spring Session. @@ -38,6 +39,12 @@ public class HazelcastSessionProperties { */ private FlushMode flushMode = FlushMode.ON_SAVE; + /** + * Sessions save mode. Determines how session changes are tracked and saved to the + * session store. + */ + private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; + public String getMapName() { return this.mapName; } @@ -54,4 +61,12 @@ public class HazelcastSessionProperties { this.flushMode = flushMode; } + public SaveMode getSaveMode() { + return this.saveMode; + } + + public void setSaveMode(SaveMode saveMode) { + this.saveMode = saveMode; + } + } 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 88371ab426..249e0a8c44 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 @@ -67,6 +67,7 @@ class JdbcSessionConfiguration { } setTableName(jdbcSessionProperties.getTableName()); setCleanupCron(jdbcSessionProperties.getCleanupCron()); + setSaveMode(jdbcSessionProperties.getSaveMode()); } } 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 f0e263f5cf..7ac12ff838 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.session; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceInitializationMode; +import org.springframework.session.SaveMode; /** * Configuration properties for JDBC backed Spring Session. @@ -55,6 +56,12 @@ public class JdbcSessionProperties { */ private DataSourceInitializationMode initializeSchema = DataSourceInitializationMode.EMBEDDED; + /** + * Sessions save mode. Determines how session changes are tracked and saved to the + * session store. + */ + private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; + public String getSchema() { return this.schema; } @@ -87,4 +94,12 @@ public class JdbcSessionProperties { this.initializeSchema = initializeSchema; } + public SaveMode getSaveMode() { + return this.saveMode; + } + + public void setSaveMode(SaveMode saveMode) { + this.saveMode = saveMode; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisReactiveSessionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisReactiveSessionConfiguration.java index f43c7b3568..6470bef404 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisReactiveSessionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/RedisReactiveSessionConfiguration.java @@ -53,6 +53,7 @@ class RedisReactiveSessionConfiguration { setMaxInactiveIntervalInSeconds((int) timeout.getSeconds()); } setRedisNamespace(redisSessionProperties.getNamespace()); + setSaveMode(redisSessionProperties.getSaveMode()); } } 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 3f830e6614..c93a9bf21d 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 @@ -76,6 +76,7 @@ class RedisSessionConfiguration { setRedisNamespace(redisSessionProperties.getNamespace()); setFlushMode(redisSessionProperties.getFlushMode()); setCleanupCron(redisSessionProperties.getCleanupCron()); + setSaveMode(redisSessionProperties.getSaveMode()); } } 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 ac75abf358..fef324a085 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 @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.session; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.session.FlushMode; +import org.springframework.session.SaveMode; /** * Configuration properties for Redis backed Spring Session. @@ -51,6 +52,12 @@ public class RedisSessionProperties { */ private String cleanupCron = DEFAULT_CLEANUP_CRON; + /** + * Sessions save mode. Determines how session changes are tracked and saved to the + * session store. + */ + private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; + public String getNamespace() { return this.namespace; } @@ -83,6 +90,14 @@ public class RedisSessionProperties { this.configureAction = configureAction; } + public SaveMode getSaveMode() { + return this.saveMode; + } + + public void setSaveMode(SaveMode saveMode) { + this.saveMode = saveMode; + } + /** * Strategies for configuring and validating Redis. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java index 6d90ff6e9b..04ae02e767 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java @@ -25,6 +25,7 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.session.SaveMode; import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository; import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository; @@ -47,7 +48,7 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf this.contextRunner.withPropertyValues("spring.session.store-type=redis") .withConfiguration( AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) - .run(validateSpringSessionUsesRedis("spring:session:")); + .run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE)); } @Test @@ -55,7 +56,7 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf this.contextRunner.withClassLoader(new FilteredClassLoader(ReactiveMongoOperationsSessionRepository.class)) .withConfiguration( AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) - .run(validateSpringSessionUsesRedis("spring:session:")); + .run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE)); } @Test @@ -63,15 +64,18 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf this.contextRunner .withConfiguration( AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) - .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo") - .run(validateSpringSessionUsesRedis("foo:")); + .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo", + "spring.session.redis.save-mode=on-get-attribute") + .run(validateSpringSessionUsesRedis("foo:", SaveMode.ON_GET_ATTRIBUTE)); } - private ContextConsumer validateSpringSessionUsesRedis(String namespace) { + private ContextConsumer validateSpringSessionUsesRedis(String namespace, + SaveMode saveMode) { return (context) -> { ReactiveRedisOperationsSessionRepository repository = validateSessionRepository(context, ReactiveRedisOperationsSessionRepository.class); assertThat(repository).hasFieldOrPropertyWithValue("namespace", namespace); + assertThat(repository).hasFieldOrPropertyWithValue("saveMode", saveMode); }; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationHazelcastTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationHazelcastTests.java index edad126edc..980de4f6f2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationHazelcastTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationHazelcastTests.java @@ -27,6 +27,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.FlushMode; +import org.springframework.session.SaveMode; import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.hazelcast.HazelcastSessionRepository; @@ -88,6 +89,16 @@ class SessionAutoConfigurationHazelcastTests extends AbstractSessionAutoConfigur }); } + @Test + void customSaveMode() { + this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast", + "spring.session.hazelcast.save-mode=on-get-attribute").run((context) -> { + HazelcastSessionRepository repository = validateSessionRepository(context, + HazelcastSessionRepository.class); + assertThat(repository).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE); + }); + } + @Configuration(proxyBeanMethods = false) static class HazelcastConfiguration { 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 e959fd6842..7653898b0b 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 @@ -30,6 +30,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.session.SaveMode; import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.hazelcast.HazelcastSessionRepository; @@ -129,4 +130,17 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration }); } + @Test + void customSaveMode() { + this.contextRunner + .withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.save-mode=on-get-attribute") + .run((context) -> { + assertThat(context.getBean(JdbcSessionProperties.class).getSaveMode()) + .isEqualTo(SaveMode.ON_GET_ATTRIBUTE); + SpringBootJdbcHttpSessionConfiguration configuration = context + .getBean(SpringBootJdbcHttpSessionConfiguration.class); + assertThat(configuration).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE); + }); + } + } 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 0d13cdb818..96f8416ad1 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 @@ -33,6 +33,7 @@ import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.session.FlushMode; +import org.springframework.session.SaveMode; import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction; @@ -64,8 +65,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio .withPropertyValues("spring.session.store-type=redis", "spring.redis.port=" + redis.getFirstMappedPort()) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) - .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, - "0 * * * * *")); + .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, "0 * * * * *", + SaveMode.ON_SET_ATTRIBUTE)); } @Test @@ -75,8 +76,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio JdbcOperationsSessionRepository.class, MongoOperationsSessionRepository.class)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .withPropertyValues("spring.redis.port=" + redis.getFirstMappedPort()) - .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, - "0 * * * * *")); + .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, "0 * * * * *", + SaveMode.ON_SET_ATTRIBUTE)); } @Test @@ -84,8 +85,10 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio this.contextRunner.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.cleanup-cron=0 0 12 * * *", + "spring.session.redis.save-mode=on-get-attribute", "spring.redis.port=" + redis.getFirstMappedPort()) - .run(validateSpringSessionUsesRedis("foo:event:0:created:", FlushMode.IMMEDIATE, "0 0 12 * * *")); + .run(validateSpringSessionUsesRedis("foo:event:0:created:", FlushMode.IMMEDIATE, "0 0 12 * * *", + SaveMode.ON_GET_ATTRIBUTE)); } @Test @@ -116,7 +119,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio } private ContextConsumer validateSpringSessionUsesRedis( - String sessionCreatedChannelPrefix, FlushMode flushMode, String cleanupCron) { + String sessionCreatedChannelPrefix, FlushMode flushMode, String cleanupCron, SaveMode saveMode) { return (context) -> { RedisOperationsSessionRepository repository = validateSessionRepository(context, RedisOperationsSessionRepository.class); @@ -125,6 +128,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio SpringBootRedisHttpSessionConfiguration configuration = context .getBean(SpringBootRedisHttpSessionConfiguration.class); assertThat(configuration).hasFieldOrPropertyWithValue("cleanupCron", cleanupCron); + assertThat(repository).hasFieldOrPropertyWithValue("saveMode", saveMode); }; }