From a0fabc98512b4897936b7c2308289d35e9f30fbe Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Thu, 30 May 2019 17:53:07 +0300 Subject: [PATCH 1/2] Add session property for ConfigureRedisAction Add a new property to `RedisSessionProperties` that allows the default `ConfigureRedisAction` to be changed. Users can still also configure the action using a `@Bean`. See gh-17022 --- .../session/RedisSessionConfiguration.java | 17 ++++++ .../session/RedisSessionProperties.java | 32 ++++++++++- .../SessionAutoConfigurationRedisTests.java | 56 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) 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 78282c76cd..78e2d53cf2 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 @@ -22,13 +22,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.session.RedisSessionProperties.ConfigurationStrategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.session.SessionRepository; import org.springframework.session.data.redis.RedisOperationsSessionRepository; +import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction; +import org.springframework.session.data.redis.config.ConfigureRedisAction; import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration; /** @@ -62,6 +66,19 @@ class RedisSessionConfiguration { setCleanupCron(redisSessionProperties.getCleanupCron()); } + @Bean + @ConditionalOnMissingBean + public ConfigureRedisAction configureRedisAction(RedisSessionProperties redisSessionProperties) { + ConfigurationStrategy strategy = redisSessionProperties.getConfigurationStrategy(); + if (strategy == ConfigurationStrategy.NOTIFY_KEYSPACE_EVENTS) { + return new ConfigureNotifyKeyspaceEventsAction(); + } + if (strategy == ConfigurationStrategy.NO_OP) { + return ConfigureRedisAction.NO_OP; + } + throw new IllegalStateException("Strategy '" + strategy + "' is not supported."); + } + } } 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 a617480c8e..aebe8b1184 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 @@ -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. @@ -40,6 +40,11 @@ public class RedisSessionProperties { */ private RedisFlushMode flushMode = RedisFlushMode.ON_SAVE; + /** + * Allows specifying a strategy for configuring and validating Redis. + */ + private ConfigurationStrategy configurationStrategy = ConfigurationStrategy.NOTIFY_KEYSPACE_EVENTS; + /** * Cron expression for expired session cleanup job. */ @@ -69,4 +74,29 @@ public class RedisSessionProperties { this.cleanupCron = cleanupCron; } + public ConfigurationStrategy getConfigurationStrategy() { + return this.configurationStrategy; + } + + public void setConfigurationStrategy(ConfigurationStrategy configurationStrategy) { + this.configurationStrategy = configurationStrategy; + } + + /** + * Allows specifying a strategy for configuring and validating Redis. + */ + public enum ConfigurationStrategy { + + /** + * Do nothing. + */ + NO_OP, + /** + * Ensures that Redis Keyspace events for Generic commands and Expired events are + * enabled. + */ + NOTIFY_KEYSPACE_EVENTS + + } + } 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 b96028db23..a9a6220198 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 @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.session; +import java.util.Map; + import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; @@ -28,13 +30,18 @@ import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.testsupport.testcontainers.DisabledWithoutDockerTestcontainers; 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.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.redis.RedisFlushMode; import org.springframework.session.data.redis.RedisOperationsSessionRepository; +import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction; +import org.springframework.session.data.redis.config.ConfigureRedisAction; import org.springframework.session.hazelcast.HazelcastSessionRepository; import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Redis specific tests for {@link SessionAutoConfiguration}. @@ -81,6 +88,34 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio .run(validateSpringSessionUsesRedis("foo:event:0:created:", RedisFlushMode.IMMEDIATE, "0 0 12 * * *")); } + @Test + void redisSessionConfigureNoStrategy() { + this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) + .withPropertyValues("spring.session.store-type=redis", + "spring.session.redis.configuration-strategy=no_op", + "spring.redis.port=" + redis.getFirstMappedPort()) + .run(validateStrategy(ConfigureRedisAction.NO_OP.getClass())); + } + + @Test + void redisSessionConfigureDefaultStrategy() { + this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) + .withPropertyValues("spring.session.store-type=redis", + "spring.redis.port=" + redis.getFirstMappedPort()) + .run(validateStrategy(ConfigureNotifyKeyspaceEventsAction.class, + entry("notify-keyspace-events", "gxE"))); + } + + @Test + void redisSessionConfigureCustomStrategy() { + this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) + .withUserConfiguration(MaxEntriesRedisAction.class) + .withPropertyValues("spring.session.store-type=redis", + "spring.redis.port=" + redis.getFirstMappedPort()) + .run(validateStrategy(MaxEntriesRedisAction.class, entry("set-max-intset-entries", "1024"))); + + } + private ContextConsumer validateSpringSessionUsesRedis( String sessionCreatedChannelPrefix, RedisFlushMode flushMode, String cleanupCron) { return (context) -> { @@ -94,4 +129,25 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio }; } + private ContextConsumer validateStrategy( + Class actionClass, Map.Entry... values) { + return (context) -> { + assertThat(context).hasSingleBean(ConfigureRedisAction.class).hasSingleBean(RedisConnectionFactory.class); + assertThat(context.getBean(ConfigureRedisAction.class)).isInstanceOf(actionClass); + RedisConnection connection = context.getBean(RedisConnectionFactory.class).getConnection(); + if (values.length > 0) { + assertThat(connection.getConfig("*")).contains(values); + } + }; + } + + static class MaxEntriesRedisAction implements ConfigureRedisAction { + + @Override + public void configure(RedisConnection connection) { + connection.setConfig("set-max-intset-entries", "1024"); + } + + } + } From 919913ae669b5ff211e25b400c1015d6f6fba472 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 12 Jun 2019 17:33:45 -0700 Subject: [PATCH 2/2] Polish "Add session property for ConfigureRedisAction" See gh-17022 --- .../session/RedisSessionConfiguration.java | 27 ++++++++--------- .../session/RedisSessionProperties.java | 30 ++++++++++--------- .../SessionAutoConfigurationRedisTests.java | 14 ++++----- 3 files changed, 36 insertions(+), 35 deletions(-) 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 78e2d53cf2..810916f627 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 @@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.session.RedisSessionProperties.ConfigurationStrategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -52,6 +51,19 @@ import org.springframework.session.data.redis.config.annotation.web.http.RedisHt @EnableConfigurationProperties(RedisSessionProperties.class) class RedisSessionConfiguration { + @Bean + @ConditionalOnMissingBean + public ConfigureRedisAction configureRedisAction(RedisSessionProperties redisSessionProperties) { + switch (redisSessionProperties.getConfigurationStrategy()) { + case NOTIFY_KEYSPACE_EVENTS: + return new ConfigureNotifyKeyspaceEventsAction(); + case NONE: + return ConfigureRedisAction.NO_OP; + } + throw new IllegalStateException("Unsupported redis configuration strategy '" + + redisSessionProperties.getConfigurationStrategy() + "'."); + } + @Configuration public static class SpringBootRedisHttpSessionConfiguration extends RedisHttpSessionConfiguration { @@ -66,19 +78,6 @@ class RedisSessionConfiguration { setCleanupCron(redisSessionProperties.getCleanupCron()); } - @Bean - @ConditionalOnMissingBean - public ConfigureRedisAction configureRedisAction(RedisSessionProperties redisSessionProperties) { - ConfigurationStrategy strategy = redisSessionProperties.getConfigurationStrategy(); - if (strategy == ConfigurationStrategy.NOTIFY_KEYSPACE_EVENTS) { - return new ConfigureNotifyKeyspaceEventsAction(); - } - if (strategy == ConfigurationStrategy.NO_OP) { - return ConfigureRedisAction.NO_OP; - } - throw new IllegalStateException("Strategy '" + strategy + "' is not supported."); - } - } } 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 aebe8b1184..ed04db251a 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 @@ -41,9 +41,10 @@ public class RedisSessionProperties { private RedisFlushMode flushMode = RedisFlushMode.ON_SAVE; /** - * Allows specifying a strategy for configuring and validating Redis. + * The configure action to apply when no user defined ConfigureRedisAction bean is + * present. */ - private ConfigurationStrategy configurationStrategy = ConfigurationStrategy.NOTIFY_KEYSPACE_EVENTS; + private ConfigureAction configureAction = ConfigureAction.NOTIFY_KEYSPACE_EVENTS; /** * Cron expression for expired session cleanup job. @@ -74,28 +75,29 @@ public class RedisSessionProperties { this.cleanupCron = cleanupCron; } - public ConfigurationStrategy getConfigurationStrategy() { - return this.configurationStrategy; + public ConfigureAction getConfigurationStrategy() { + return this.configureAction; } - public void setConfigurationStrategy(ConfigurationStrategy configurationStrategy) { - this.configurationStrategy = configurationStrategy; + public void setConfigurationStrategy(ConfigureAction configurationStrategy) { + this.configureAction = configurationStrategy; } /** - * Allows specifying a strategy for configuring and validating Redis. + * Strategies for configuring and validating Redis. */ - public enum ConfigurationStrategy { + public enum ConfigureAction { /** - * Do nothing. - */ - NO_OP, - /** - * Ensures that Redis Keyspace events for Generic commands and Expired events are + * Ensure that Redis Keyspace events for Generic commands and Expired events are * enabled. */ - NOTIFY_KEYSPACE_EVENTS + NOTIFY_KEYSPACE_EVENTS, + + /** + * No not attempt to apply any custom Redis configuration. + */ + NONE } 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 a9a6220198..8bfbcc9167 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 @@ -91,8 +91,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio @Test void redisSessionConfigureNoStrategy() { this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) - .withPropertyValues("spring.session.store-type=redis", - "spring.session.redis.configuration-strategy=no_op", + .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.configure-action=none", "spring.redis.port=" + redis.getFirstMappedPort()) .run(validateStrategy(ConfigureRedisAction.NO_OP.getClass())); } @@ -130,13 +129,14 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio } private ContextConsumer validateStrategy( - Class actionClass, Map.Entry... values) { + Class expectedConfigureRedisActionType, Map.Entry... expectedConfig) { return (context) -> { - assertThat(context).hasSingleBean(ConfigureRedisAction.class).hasSingleBean(RedisConnectionFactory.class); - assertThat(context.getBean(ConfigureRedisAction.class)).isInstanceOf(actionClass); + assertThat(context).hasSingleBean(ConfigureRedisAction.class); + assertThat(context).hasSingleBean(RedisConnectionFactory.class); + assertThat(context.getBean(ConfigureRedisAction.class)).isInstanceOf(expectedConfigureRedisActionType); RedisConnection connection = context.getBean(RedisConnectionFactory.class).getConnection(); - if (values.length > 0) { - assertThat(connection.getConfig("*")).contains(values); + if (expectedConfig.length > 0) { + assertThat(connection.getConfig("*")).contains(expectedConfig); } }; }