Add support for configuring Spring Session SaveMode

See gh-17514
pull/17538/head
Vedran Pavic 5 years ago committed by Stephane Nicoll
parent b7d349db83
commit e073792448

@ -59,6 +59,7 @@ class HazelcastSessionConfiguration {
} }
setSessionMapName(hazelcastSessionProperties.getMapName()); setSessionMapName(hazelcastSessionProperties.getMapName());
setFlushMode(hazelcastSessionProperties.getFlushMode()); setFlushMode(hazelcastSessionProperties.getFlushMode());
setSaveMode(hazelcastSessionProperties.getSaveMode());
} }
} }

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.session;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.session.FlushMode; import org.springframework.session.FlushMode;
import org.springframework.session.SaveMode;
/** /**
* Configuration properties for Hazelcast backed Spring Session. * Configuration properties for Hazelcast backed Spring Session.
@ -38,6 +39,12 @@ public class HazelcastSessionProperties {
*/ */
private FlushMode flushMode = FlushMode.ON_SAVE; 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() { public String getMapName() {
return this.mapName; return this.mapName;
} }
@ -54,4 +61,12 @@ public class HazelcastSessionProperties {
this.flushMode = flushMode; this.flushMode = flushMode;
} }
public SaveMode getSaveMode() {
return this.saveMode;
}
public void setSaveMode(SaveMode saveMode) {
this.saveMode = saveMode;
}
} }

@ -67,6 +67,7 @@ class JdbcSessionConfiguration {
} }
setTableName(jdbcSessionProperties.getTableName()); setTableName(jdbcSessionProperties.getTableName());
setCleanupCron(jdbcSessionProperties.getCleanupCron()); setCleanupCron(jdbcSessionProperties.getCleanupCron());
setSaveMode(jdbcSessionProperties.getSaveMode());
} }
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.jdbc.DataSourceInitializationMode;
import org.springframework.session.SaveMode;
/** /**
* Configuration properties for JDBC backed Spring Session. * Configuration properties for JDBC backed Spring Session.
@ -55,6 +56,12 @@ public class JdbcSessionProperties {
*/ */
private DataSourceInitializationMode initializeSchema = DataSourceInitializationMode.EMBEDDED; 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() { public String getSchema() {
return this.schema; return this.schema;
} }
@ -87,4 +94,12 @@ public class JdbcSessionProperties {
this.initializeSchema = initializeSchema; this.initializeSchema = initializeSchema;
} }
public SaveMode getSaveMode() {
return this.saveMode;
}
public void setSaveMode(SaveMode saveMode) {
this.saveMode = saveMode;
}
} }

@ -53,6 +53,7 @@ class RedisReactiveSessionConfiguration {
setMaxInactiveIntervalInSeconds((int) timeout.getSeconds()); setMaxInactiveIntervalInSeconds((int) timeout.getSeconds());
} }
setRedisNamespace(redisSessionProperties.getNamespace()); setRedisNamespace(redisSessionProperties.getNamespace());
setSaveMode(redisSessionProperties.getSaveMode());
} }
} }

@ -76,6 +76,7 @@ class RedisSessionConfiguration {
setRedisNamespace(redisSessionProperties.getNamespace()); setRedisNamespace(redisSessionProperties.getNamespace());
setFlushMode(redisSessionProperties.getFlushMode()); setFlushMode(redisSessionProperties.getFlushMode());
setCleanupCron(redisSessionProperties.getCleanupCron()); setCleanupCron(redisSessionProperties.getCleanupCron());
setSaveMode(redisSessionProperties.getSaveMode());
} }
} }

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.session;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.session.FlushMode; import org.springframework.session.FlushMode;
import org.springframework.session.SaveMode;
/** /**
* Configuration properties for Redis backed Spring Session. * Configuration properties for Redis backed Spring Session.
@ -51,6 +52,12 @@ public class RedisSessionProperties {
*/ */
private String cleanupCron = DEFAULT_CLEANUP_CRON; 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() { public String getNamespace() {
return this.namespace; return this.namespace;
} }
@ -83,6 +90,14 @@ public class RedisSessionProperties {
this.configureAction = configureAction; this.configureAction = configureAction;
} }
public SaveMode getSaveMode() {
return this.saveMode;
}
public void setSaveMode(SaveMode saveMode) {
this.saveMode = saveMode;
}
/** /**
* Strategies for configuring and validating Redis. * Strategies for configuring and validating Redis.
*/ */

@ -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.assertj.AssertableReactiveWebApplicationContext;
import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; 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.mongo.ReactiveMongoOperationsSessionRepository;
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository; import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
@ -47,7 +48,7 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf
this.contextRunner.withPropertyValues("spring.session.store-type=redis") this.contextRunner.withPropertyValues("spring.session.store-type=redis")
.withConfiguration( .withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:")); .run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
} }
@Test @Test
@ -55,7 +56,7 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf
this.contextRunner.withClassLoader(new FilteredClassLoader(ReactiveMongoOperationsSessionRepository.class)) this.contextRunner.withClassLoader(new FilteredClassLoader(ReactiveMongoOperationsSessionRepository.class))
.withConfiguration( .withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:")); .run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
} }
@Test @Test
@ -63,15 +64,18 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf
this.contextRunner this.contextRunner
.withConfiguration( .withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class)) AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo") .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo",
.run(validateSpringSessionUsesRedis("foo:")); "spring.session.redis.save-mode=on-get-attribute")
.run(validateSpringSessionUsesRedis("foo:", SaveMode.ON_GET_ATTRIBUTE));
} }
private ContextConsumer<AssertableReactiveWebApplicationContext> validateSpringSessionUsesRedis(String namespace) { private ContextConsumer<AssertableReactiveWebApplicationContext> validateSpringSessionUsesRedis(String namespace,
SaveMode saveMode) {
return (context) -> { return (context) -> {
ReactiveRedisOperationsSessionRepository repository = validateSessionRepository(context, ReactiveRedisOperationsSessionRepository repository = validateSessionRepository(context,
ReactiveRedisOperationsSessionRepository.class); ReactiveRedisOperationsSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("namespace", namespace); assertThat(repository).hasFieldOrPropertyWithValue("namespace", namespace);
assertThat(repository).hasFieldOrPropertyWithValue("saveMode", saveMode);
}; };
} }

@ -27,6 +27,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.session.FlushMode; import org.springframework.session.FlushMode;
import org.springframework.session.SaveMode;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository; 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) @Configuration(proxyBeanMethods = false)
static class HazelcastConfiguration { static class HazelcastConfiguration {

@ -30,6 +30,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.session.SaveMode;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository; 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);
});
}
} }

@ -33,6 +33,7 @@ import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.FlushMode; import org.springframework.session.FlushMode;
import org.springframework.session.SaveMode;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction; import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
@ -64,8 +65,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
.withPropertyValues("spring.session.store-type=redis", .withPropertyValues("spring.session.store-type=redis",
"spring.redis.port=" + redis.getFirstMappedPort()) "spring.redis.port=" + redis.getFirstMappedPort())
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, "0 * * * * *",
"0 * * * * *")); SaveMode.ON_SET_ATTRIBUTE));
} }
@Test @Test
@ -75,8 +76,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
JdbcOperationsSessionRepository.class, MongoOperationsSessionRepository.class)) JdbcOperationsSessionRepository.class, MongoOperationsSessionRepository.class))
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.redis.port=" + redis.getFirstMappedPort()) .withPropertyValues("spring.redis.port=" + redis.getFirstMappedPort())
.run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, .run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE, "0 * * * * *",
"0 * * * * *")); SaveMode.ON_SET_ATTRIBUTE));
} }
@Test @Test
@ -84,8 +85,10 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo", .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.flush-mode=immediate", "spring.session.redis.cleanup-cron=0 0 12 * * *",
"spring.session.redis.save-mode=on-get-attribute",
"spring.redis.port=" + redis.getFirstMappedPort()) "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 @Test
@ -116,7 +119,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
} }
private ContextConsumer<AssertableWebApplicationContext> validateSpringSessionUsesRedis( private ContextConsumer<AssertableWebApplicationContext> validateSpringSessionUsesRedis(
String sessionCreatedChannelPrefix, FlushMode flushMode, String cleanupCron) { String sessionCreatedChannelPrefix, FlushMode flushMode, String cleanupCron, SaveMode saveMode) {
return (context) -> { return (context) -> {
RedisOperationsSessionRepository repository = validateSessionRepository(context, RedisOperationsSessionRepository repository = validateSessionRepository(context,
RedisOperationsSessionRepository.class); RedisOperationsSessionRepository.class);
@ -125,6 +128,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
SpringBootRedisHttpSessionConfiguration configuration = context SpringBootRedisHttpSessionConfiguration configuration = context
.getBean(SpringBootRedisHttpSessionConfiguration.class); .getBean(SpringBootRedisHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("cleanupCron", cleanupCron); assertThat(configuration).hasFieldOrPropertyWithValue("cleanupCron", cleanupCron);
assertThat(repository).hasFieldOrPropertyWithValue("saveMode", saveMode);
}; };
} }

Loading…
Cancel
Save