diff --git a/docs/build.gradle b/docs/build.gradle
index 472a607..10b5675 100644
--- a/docs/build.gradle
+++ b/docs/build.gradle
@@ -31,6 +31,7 @@ asciidoctor {
'spring-session-version' : version,
'spring-version' : springVersion,
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
+ 'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/',
'samples-dir' : rootProject.projectDir.path + '/samples/',
'source-highlighter' : 'coderay',
diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc
index 0b1b20d..8ea48a0 100644
--- a/docs/src/docs/asciidoc/index.adoc
+++ b/docs/src/docs/asciidoc/index.adoc
@@ -364,13 +364,30 @@ Firing a `SessionDestroyedEvent` is made available through the `SessionMessageLi
In order for this to work, Redis Keyspace events for Generic commands and Expired events needs to be enabled.
For example:
-TIP: If you are using `@EnableRedisHttpSession` the `SessionMessageListener` and enabling the necessary Redis Keyspace events is done automatically.
-
[source,bash]
----
redis-cli config set notify-keyspace-events Egx
----
+If you are using `@EnableRedisHttpSession` the `SessionMessageListener` and enabling the necessary Redis Keyspace events is done automatically.
+However, in a secured Redis enviornment the config command is disabled.
+This means that Spring Session cannot configure Redis Keyspace events for you.
+To disable the automatic configuration add `ConfigureRedisAction.NO_OP` as a bean.
+
+For example, Java Configuration can use the following:
+
+[source,java,indent=0]
+----
+include::{docs-test-dir}docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java[tags=configure-redis-action]
+----
+
+XML Configuraiton can use the following:
+
+[source,xml,indent=0]
+----
+include::{docs-test-resources-dir}docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests-context.xml[tags=configure-redis-action]
+----
+
[[api-redisoperationssessionrepository-cli]]
==== Viewing the Session in Redis
diff --git a/docs/src/test/java/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests.java b/docs/src/test/java/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests.java
new file mode 100644
index 0000000..55b97e8
--- /dev/null
+++ b/docs/src/test/java/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package docs;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.session.ExpiringSession;
+import org.springframework.session.web.http.SessionRepositoryFilter;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+/**
+ * @author Rob Winch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@WebAppConfiguration
+public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
+ @Autowired
+ SessionRepositoryFilter extends ExpiringSession> filter;
+
+ @Test
+ public void redisConnectionFactoryNotUsedSinceNoValidation() {
+ assertThat(filter).isNotNull();
+ }
+
+
+ static RedisConnectionFactory connectionFactory() {
+ return mock(RedisConnectionFactory.class);
+ }
+}
diff --git a/docs/src/test/java/docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java b/docs/src/test/java/docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java
new file mode 100644
index 0000000..2c17f2c
--- /dev/null
+++ b/docs/src/test/java/docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package docs;
+
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+/**
+ * @author Rob Winch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@WebAppConfiguration
+public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
+
+ @Test
+ public void redisConnectionFactoryNotUsedSinceNoValidation() {}
+
+ @EnableRedisHttpSession
+ @Configuration
+ static class Config {
+
+ // tag::configure-redis-action[]
+ @Bean
+ public static ConfigureRedisAction configureRedisAction() {
+ return ConfigureRedisAction.NO_OP;
+ }
+ // end::configure-redis-action[]
+
+ @Bean
+ public RedisConnectionFactory redisConnectionFactory() {
+ return mock(RedisConnectionFactory.class);
+ }
+ }
+}
diff --git a/docs/src/test/resources/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests-context.xml b/docs/src/test/resources/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests-context.xml
new file mode 100644
index 0000000..11f16ec
--- /dev/null
+++ b/docs/src/test/resources/docs/HttpSessionConfigurationNoOpConfigureRedisActionXmlTests-context.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureNotifyKeyspaceEventsAction.java b/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureNotifyKeyspaceEventsAction.java
new file mode 100644
index 0000000..2a6ea15
--- /dev/null
+++ b/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureNotifyKeyspaceEventsAction.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.session.data.redis.config;
+
+import java.util.List;
+
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.data.redis.connection.RedisConnection;
+
+/**
+ *
+ * Ensures that Redis Keyspace events for Generic commands and Expired events are enabled.
+ * For example, it might set the following:
+ *
+ *
+ *
+ * config set notify-keyspace-events Egx
+ *
+ *
+ *
+ * This strategy will not work if the Redis instance has been properly secured. Instead,
+ * the Redis instance should be configured externally and a Bean of type
+ * {@link ConfigureRedisAction#NO_OP} should be exposed.
+ *
+ *
+ * @author Rob Winch
+ * @since 1.0.1
+ */
+public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction {
+
+ static final String CONFIG_NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events";
+
+ /* (non-Javadoc)
+ * @see org.springframework.session.data.redis.config.ConfigureRedisAction#configure(org.springframework.data.redis.connection.RedisConnection)
+ */
+ public void configure(RedisConnection connection) {
+ String notifyOptions = getNotifyOptions(connection);
+ String customizedNotifyOptions = notifyOptions;
+ if(!customizedNotifyOptions.contains("E")) {
+ customizedNotifyOptions += "E";
+ }
+ boolean A = customizedNotifyOptions.contains("A");
+ if(!(A || customizedNotifyOptions.contains("g"))) {
+ customizedNotifyOptions += "g";
+ }
+ if(!(A || customizedNotifyOptions.contains("x"))) {
+ customizedNotifyOptions += "x";
+ }
+ if(!notifyOptions.equals(customizedNotifyOptions)) {
+ connection.setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
+ }
+ }
+
+ private String getNotifyOptions(RedisConnection connection) {
+ try {
+ List config = connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS);
+ if(config.size() < 2) {
+ return "";
+ }
+ return config.get(1);
+ } catch(InvalidDataAccessApiUsageException e) {
+ throw new IllegalStateException("Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent", e);
+ }
+ }
+
+}
diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureRedisAction.java b/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureRedisAction.java
new file mode 100644
index 0000000..28fabbb
--- /dev/null
+++ b/spring-session/src/main/java/org/springframework/session/data/redis/config/ConfigureRedisAction.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.session.data.redis.config;
+
+import org.springframework.data.redis.connection.RedisConnection;
+
+/**
+ * Allows specifying a strategy for configuring and validating Redis.
+ *
+ * @author Rob Winch
+ * @since 1.0.1
+ */
+public interface ConfigureRedisAction {
+
+ void configure(RedisConnection connection);
+
+ /**
+ * A do nothing implementation of {@link ConfigureRedisAction}.
+ */
+ ConfigureRedisAction NO_OP = new ConfigureRedisAction() {
+
+ public void configure(RedisConnection connection) {
+ }
+ };
+}
diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.java b/spring-session/src/main/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.java
index 42c8555..fdae896 100644
--- a/spring-session/src/main/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.java
+++ b/spring-session/src/main/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.java
@@ -16,7 +16,6 @@
package org.springframework.session.data.redis.config.annotation.web.http;
import java.util.Arrays;
-import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
@@ -42,6 +41,8 @@ import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.SessionMessageListener;
+import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.web.http.HttpSessionStrategy;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.ClassUtils;
@@ -69,6 +70,8 @@ public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoad
@Autowired
private ApplicationEventPublisher eventPublisher;
+ private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
+
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory) {
@@ -141,7 +144,7 @@ public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoad
@Bean
public EnableRedisKeyspaceNotificationsInitializer enableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory) {
- return new EnableRedisKeyspaceNotificationsInitializer(connectionFactory);
+ return new EnableRedisKeyspaceNotificationsInitializer(connectionFactory, configureRedisAction);
}
/**
@@ -154,38 +157,28 @@ public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoad
private final RedisConnectionFactory connectionFactory;
- EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory) {
+ private ConfigureRedisAction configure;
+
+ EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory, ConfigureRedisAction configure) {
this.connectionFactory = connectionFactory;
+ this.configure = configure;
}
public void afterPropertiesSet() throws Exception {
RedisConnection connection = connectionFactory.getConnection();
- String notifyOptions = getNotifyOptions(connection);
- String customizedNotifyOptions = notifyOptions;
- if(!customizedNotifyOptions.contains("E")) {
- customizedNotifyOptions += "E";
- }
- boolean A = customizedNotifyOptions.contains("A");
- if(!(A || customizedNotifyOptions.contains("g"))) {
- customizedNotifyOptions += "g";
- }
- if(!(A || customizedNotifyOptions.contains("x"))) {
- customizedNotifyOptions += "x";
- }
- if(!notifyOptions.equals(customizedNotifyOptions)) {
- connection.setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
- }
- }
-
- private String getNotifyOptions(RedisConnection connection) {
- List config = connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS);
- if(config.size() < 2) {
- return "";
- }
- return config.get(1);
+ configure.configure(connection);
}
}
+ /**
+ * Sets the action to perform for configuring Redis.
+ *
+ * @param configureRedisAction the configureRedis to set. The default is {@link ConfigureNotifyKeyspaceEventsAction}.
+ */
+ @Autowired(required = false)
+ public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) {
+ this.configureRedisAction = configureRedisAction;
+ }
/* (non-Javadoc)
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
diff --git a/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisKeyspaceNotificationsInitializerTests.java b/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisKeyspaceNotificationsInitializerTests.java
index dcf691a..c1ffe26 100644
--- a/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisKeyspaceNotificationsInitializerTests.java
+++ b/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisKeyspaceNotificationsInitializerTests.java
@@ -30,6 +30,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
import java.util.Arrays;
@@ -50,7 +51,7 @@ public class EnableRedisKeyspaceNotificationsInitializerTests {
public void setup() {
when(connectionFactory.getConnection()).thenReturn(connection);
- initializer = new EnableRedisKeyspaceNotificationsInitializer(connectionFactory);
+ initializer = new EnableRedisKeyspaceNotificationsInitializer(connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
}
@Test
diff --git a/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java b/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java
new file mode 100644
index 0000000..521947d
--- /dev/null
+++ b/spring-session/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.session.data.redis.config.annotation.web.http;
+
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+/**
+ * @author Rob Winch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@WebAppConfiguration
+public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
+
+ @Test
+ public void redisConnectionFactoryNotUsedSinceNoValidation() {}
+
+ @EnableRedisHttpSession
+ @Configuration
+ static class Config {
+
+ @Bean
+ public static ConfigureRedisAction configureRedisAction() {
+ return ConfigureRedisAction.NO_OP;
+ }
+
+ @Bean
+ public RedisConnectionFactory redisConnectionFactory() {
+ return mock(RedisConnectionFactory.class);
+ }
+ }
+}