Support placeholder resolution for redisNamespace in EnableRedisHttpSession annotation

Fixes gh-381
This commit is contained in:
Eddú Meléndez
2016-04-23 17:54:44 +10:00
committed by Rob Winch
parent 6424910c83
commit 2d359986d3
3 changed files with 175 additions and 55 deletions

View File

@@ -26,6 +26,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
@@ -49,6 +50,7 @@ import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/**
* Exposes the {@link SessionRepositoryFilter} as a bean named
@@ -56,14 +58,14 @@ import org.springframework.util.StringUtils;
* {@link RedisConnectionFactory} must be exposed as a Bean.
*
* @author Rob Winch
* @since 1.0
*
* @author Eddú Meléndez
* @see EnableRedisHttpSession
* @since 1.0
*/
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
implements EmbeddedValueResolverAware, ImportAware {
private Integer maxInactiveIntervalInSeconds = 1800;
@@ -79,6 +81,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
private Executor redisSubscriptionExecutor;
private StringValueResolver embeddedValueResolver;
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
@@ -162,7 +166,10 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
this.maxInactiveIntervalInSeconds = enableAttrs
.getNumber("maxInactiveIntervalInSeconds");
this.redisNamespace = enableAttrs.getString("redisNamespace");
String redisNamespaceValue = enableAttrs.getString("redisNamespace");
if (StringUtils.hasText(redisNamespaceValue)) {
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
}
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
}
@@ -177,7 +184,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
* Sets the action to perform for configuring Redis.
*
* @param configureRedisAction the configureRedis to set. The default is
* {@link ConfigureNotifyKeyspaceEventsAction}.
* {@link ConfigureNotifyKeyspaceEventsAction}.
*/
@Autowired(required = false)
public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) {
@@ -203,6 +210,10 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
/**
* Property placeholder to process the @Scheduled annotation.
* @return the {@link PropertySourcesPlaceholderConfigurer} to use

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2014-2016 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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
public class RedisHttpSessionConfigurationMockTests {
@Mock
RedisConnectionFactory factory;
@Mock
RedisConnection connection;
@Before
public void setup() {
given(this.factory.getConnection()).willReturn(this.connection);
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoOpThenNoInteractionWithConnectionFactory()
throws Exception {
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, ConfigureRedisAction.NO_OP);
init.afterPropertiesSet();
verifyZeroInteractions(this.factory);
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
willThrow(new RuntimeException()).given(action).configure(this.connection);
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
try {
init.afterPropertiesSet();
failBecauseExceptionWasNotThrown(Throwable.class);
}
catch (Throwable success) {
}
verify(this.connection).close();
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
init.afterPropertiesSet();
verify(this.connection).close();
}
}

View File

@@ -13,78 +13,97 @@
* 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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.connection.RedisConnection;
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.RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
/**
* @author Eddú Meléndez
*/
public class RedisHttpSessionConfigurationTests {
@Mock
RedisConnectionFactory factory;
@Mock
RedisConnection connection;
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
given(this.factory.getConnection()).willReturn(this.connection);
public void before() {
this.context = new AnnotationConfigApplicationContext();
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoOpThenNoInteractionWithConnectionFactory()
throws Exception {
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, ConfigureRedisAction.NO_OP);
init.afterPropertiesSet();
verifyZeroInteractions(this.factory);
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
willThrow(new RuntimeException()).given(action).configure(this.connection);
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
try {
init.afterPropertiesSet();
failBecauseExceptionWasNotThrown(Throwable.class);
@After
public void after() {
if (this.context != null) {
this.context.close();
}
catch (Throwable success) {
}
@Test
public void resolveValue() {
registerAndRefresh(RedisConfig.class, CustomRedisHttpSessionConfiguration.class);
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("myRedisNamespace");
}
@Test
public void resolveValueByPlaceholder() {
this.context.setEnvironment(new MockEnvironment().withProperty("session.redis.namespace", "customRedisNamespace"));
registerAndRefresh(RedisConfig.class, PropertySourceConfiguration.class, CustomRedisHttpSessionConfiguration2.class);
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("customRedisNamespace");
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
}
@Configuration
static class PropertySourceConfiguration {
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
verify(this.connection).close();
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
@Configuration
static class RedisConfig {
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisConnectionFactory connectionFactory = mock(RedisConnectionFactory.class);
given(connectionFactory.getConnection()).willReturn(mock(RedisConnection.class));
return connectionFactory;
}
init.afterPropertiesSet();
verify(this.connection).close();
}
@Configuration
@EnableRedisHttpSession(redisNamespace = "myRedisNamespace")
static class CustomRedisHttpSessionConfiguration {
}
@Configuration
@EnableRedisHttpSession(redisNamespace = "${session.redis.namespace}")
static class CustomRedisHttpSessionConfiguration2 {
}
}