Polish spring session auto-configuration

This commit improves the initial submission by adding more tests and
more configuration options.

Closes gh-5158
pull/5836/merge
Stephane Nicoll 9 years ago
parent 0be00e2a6d
commit bf89c8ee46

@ -16,7 +16,6 @@
package org.springframework.boot.autoconfigure.session;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@ -26,24 +25,24 @@ import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
/**
* In-memory session configuration, intended as a fallback.
* HashMap based session configuration, intended as a fallback.
*
* @author Tommy Ludwig
* @since 1.4.0
* @author Stephane Nicoll
*/
@Configuration
@EnableSpringHttpSession
@Conditional(SessionCondition.class)
class SimpleSessionConfiguration {
class HashMapSessionConfiguration {
@Bean
public SessionRepository<ExpiringSession> sessionRepository(ServerProperties serverProperties) {
public SessionRepository<ExpiringSession> sessionRepository(SessionProperties sessionProperties) {
MapSessionRepository sessionRepository = new MapSessionRepository();
Integer timeout = serverProperties.getSession().getTimeout();
if (serverProperties.getSession().getTimeout() != null) {
Integer timeout = sessionProperties.getTimeout();
if (timeout != null) {
sessionRepository.setDefaultMaxInactiveInterval(timeout);
}
return sessionRepository;
}
}

@ -16,44 +16,38 @@
package org.springframework.boot.autoconfigure.session;
import javax.annotation.PostConstruct;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.session.hazelcast.config.annotation.web.http.HazelcastHttpSessionConfiguration;
/**
* Hazelcast backed session auto-configuration.
* Hazelcast backed session configuration.
*
* @author Tommy Ludwig
* @author Eddú Meléndez
* @since 1.4.0
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnBean({ HazelcastInstance.class })
@EnableHazelcastHttpSession
@ConditionalOnBean(HazelcastInstance.class)
@Conditional(SessionCondition.class)
class HazelcastSessionConfiguration {
private ServerProperties serverProperties;
private MapSessionRepository sessionRepository;
HazelcastSessionConfiguration(ServerProperties serverProperties, MapSessionRepository sessionRepository) {
this.serverProperties = serverProperties;
this.sessionRepository = sessionRepository;
}
@PostConstruct
public void applyConfigurationProperties() {
Integer timeout = this.serverProperties.getSession().getTimeout();
if (timeout != null) {
this.sessionRepository.setDefaultMaxInactiveInterval(timeout);
@Configuration
public static class SprigBootHazelcastHttpSessionConfiguration
extends HazelcastHttpSessionConfiguration {
@Autowired
public void customize(SessionProperties sessionProperties) {
Integer timeout = sessionProperties.getTimeout();
if (timeout != null) {
setMaxInactiveIntervalInSeconds(timeout);
}
setSessionMapName(sessionProperties.getHazelcast().getMapName());
}
}
}

@ -16,44 +16,38 @@
package org.springframework.boot.autoconfigure.session;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration;
/**
* JDBC backed session auto-configuration.
* JDBC backed session configuration.
*
* @author Eddú Meléndez
* @since 1.4.0
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnBean(DataSource.class)
@EnableJdbcHttpSession
@Conditional(SessionCondition.class)
class JdbcSessionConfiguration {
private ServerProperties serverProperties;
private JdbcOperationsSessionRepository sessionRepository;
JdbcSessionConfiguration(ServerProperties serverProperties,
JdbcOperationsSessionRepository sessionRepository) {
this.serverProperties = serverProperties;
this.sessionRepository = sessionRepository;
}
@PostConstruct
public void applyConfigurationProperties() {
Integer timeout = this.serverProperties.getSession().getTimeout();
if (timeout != null) {
this.sessionRepository.setDefaultMaxInactiveInterval(timeout);
@Configuration
public static class SpringBootJdbcHttpSessionConfiguration
extends JdbcHttpSessionConfiguration {
@Autowired
public void customize(SessionProperties sessionProperties) {
Integer timeout = sessionProperties.getTimeout();
if (timeout != null) {
setMaxInactiveIntervalInSeconds(timeout);
}
setTableName(sessionProperties.getJdbc().getTableName());
}
}
}

@ -16,43 +16,37 @@
package org.springframework.boot.autoconfigure.session;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
import org.springframework.session.data.mongo.config.annotation.web.http.MongoHttpSessionConfiguration;
/**
* Mongo backed session auto-configuration.
* Mongo backed session configuration.
*
* @author Eddú Meléndez
* @since 1.4.0
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnBean(MongoOperations.class)
@EnableMongoHttpSession
@Conditional(SessionCondition.class)
class MongoSessionConfiguration {
private ServerProperties serverProperties;
private MongoOperationsSessionRepository sessionRepository;
MongoSessionConfiguration(ServerProperties serverProperties, MongoOperationsSessionRepository sessionRepository) {
this.serverProperties = serverProperties;
this.sessionRepository = sessionRepository;
}
@PostConstruct
public void applyConfigurationProperties() {
Integer timeout = this.serverProperties.getSession().getTimeout();
if (timeout != null) {
this.sessionRepository.setMaxInactiveIntervalInSeconds(timeout);
@Configuration
public static class SpringBootMongoHttpSessionConfiguration
extends MongoHttpSessionConfiguration {
@Autowired
public void customize(SessionProperties sessionProperties) {
Integer timeout = sessionProperties.getTimeout();
if (timeout != null) {
setMaxInactiveIntervalInSeconds(timeout);
}
setCollectionName(sessionProperties.getMongo().getCollectionName());
}
}
}

@ -22,11 +22,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.session.SessionRepository;
/**
* No-op session configuration used to disable Spring Session auto configuration via the
* environment.
* No-op session configuration used to disable Spring Session using the environment.
*
* @author Tommy Ludwig
* @since 1.4.0
*/
@Configuration
@ConditionalOnMissingBean(SessionRepository.class)

@ -16,45 +16,42 @@
package org.springframework.boot.autoconfigure.session;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
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.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
/**
* Redis backed session auto-configuration.
* Redis backed session configuration.
*
* @author Andy Wilkinson
* @author Tommy Ludwig
* @author Eddú Meléndez
* @since 1.4.0
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnBean({RedisTemplate.class})
@EnableRedisHttpSession
@ConditionalOnBean({ RedisTemplate.class, RedisConnectionFactory.class })
@Conditional(SessionCondition.class)
class RedisSessionConfiguration {
private ServerProperties serverProperties;
private RedisOperationsSessionRepository sessionRepository;
RedisSessionConfiguration(ServerProperties serverProperties, RedisOperationsSessionRepository sessionRepository) {
this.serverProperties = serverProperties;
this.sessionRepository = sessionRepository;
}
@PostConstruct
public void applyConfigurationProperties() {
Integer timeout = this.serverProperties.getSession().getTimeout();
if (timeout != null) {
this.sessionRepository.setDefaultMaxInactiveInterval(timeout);
@Configuration
public static class SpringBootRedisHttpSessionConfiguration
extends RedisHttpSessionConfiguration {
@Autowired
public void customize(SessionProperties sessionProperties) {
Integer timeout = sessionProperties.getTimeout();
if (timeout != null) {
setMaxInactiveIntervalInSeconds(timeout);
}
SessionProperties.Redis redis = sessionProperties.getRedis();
setRedisNamespace(redis.getNamespace());
setRedisFlushMode(redis.getFlushMode());
}
}
}

@ -21,15 +21,12 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration.SessionConfigurationImportSelector;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
@ -43,7 +40,8 @@ import org.springframework.session.SessionRepository;
* @author Andy Wilkinson
* @author Tommy Ludwig
* @author Eddú Meléndez
* @since 1.3.0
* @author Stephane Nicoll
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(Session.class)
@ -55,19 +53,6 @@ import org.springframework.session.SessionRepository;
@Import(SessionConfigurationImportSelector.class)
public class SessionAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(value = ServerProperties.class, search = SearchStrategy.CURRENT)
// Just in case user switches off ServerPropertiesAutoConfiguration
public static class ServerPropertiesConfiguration {
@Bean
// Use the same bean name as the default one for any old webapp
public ServerProperties serverProperties() {
return new ServerProperties();
}
}
/**
* {@link ImportSelector} to add {@link StoreType} configuration classes.
*/

@ -24,7 +24,7 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
/**
* General condition used by all session auto-configuration classes.
* General condition used with all session configuration classes.
*
* @author Tommy Ludwig
*/
@ -34,13 +34,13 @@ class SessionCondition extends SpringBootCondition {
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.session.store.");
if (!resolver.containsProperty("type")) {
context.getEnvironment(), "spring.session.");
if (!resolver.containsProperty("store-type")) {
return ConditionOutcome.match("Automatic session store type");
}
StoreType sessionStoreType = SessionStoreMappings
.getType(((AnnotationMetadata) metadata).getClassName());
String value = resolver.getProperty("type").replace("-", "_").toUpperCase();
String value = resolver.getProperty("store-type").replace("-", "_").toUpperCase();
if (value.equals(sessionStoreType.name())) {
return ConditionOutcome.match("Session store type " + sessionStoreType);
}

@ -16,42 +16,153 @@
package org.springframework.boot.autoconfigure.session;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.session.data.redis.RedisFlushMode;
/**
* Properties for configuring Spring Session's auto-configuration.
* Configuration properties for Spring Session.
*
* @author Tommy Ludwig
* @author Stephane Nicoll
* @since 1.4.0
*/
@ConfigurationProperties("spring.session")
public class SessionProperties {
private Store store;
/**
* Session store type, auto-detected according to the environment by default.
*/
private StoreType storeType;
private Integer timeout;
private final Hazelcast hazelcast = new Hazelcast();
public Store getStore() {
return this.store;
private final Jdbc jdbc = new Jdbc();
private final Mongo mongo = new Mongo();
private final Redis redis = new Redis();
public SessionProperties(ObjectProvider<ServerProperties> serverProperties) {
ServerProperties properties = serverProperties.getIfUnique();
this.timeout = (properties != null ? properties.getSession().getTimeout() : null);
}
public void setStore(Store store) {
this.store = store;
public StoreType getStoreType() {
return this.storeType;
}
public void setStoreType(StoreType storeType) {
this.storeType = storeType;
}
/**
* Session store-specific properties.
* Return the session timeout in seconds.
* @return the session timeout in seconds
* @see ServerProperties#getSession()
*/
public static class Store {
public Integer getTimeout() {
return this.timeout;
}
public Hazelcast getHazelcast() {
return this.hazelcast;
}
public Jdbc getJdbc() {
return this.jdbc;
}
public Mongo getMongo() {
return this.mongo;
}
public Redis getRedis() {
return this.redis;
}
public static class Hazelcast {
/**
* Session data store type, auto-detected according to the environment by default.
* Name of the map used to store sessions.
*/
private StoreType type;
private String mapName = "spring:session:sessions";
public StoreType getType() {
return this.type;
public String getMapName() {
return this.mapName;
}
public void setType(StoreType type) {
this.type = type;
public void setMapName(String mapName) {
this.mapName = mapName;
}
}
public static class Jdbc {
/**
* Name of database table used to store sessions.
*/
private String tableName = "SPRING_SESSION";
public String getTableName() {
return this.tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
public static class Mongo {
/**
* Collection name used to store sessions.
*/
private String collectionName = "sessions";
public String getCollectionName() {
return this.collectionName;
}
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
public static class Redis {
/**
* Namespace for keys used to store sessions.
*/
private String namespace = "";
/**
* Flush mode for the Redis sessions.
*/
private RedisFlushMode flushMode = RedisFlushMode.ON_SAVE;
public String getNamespace() {
return this.namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public RedisFlushMode getFlushMode() {
return this.flushMode;
}
public void setFlushMode(RedisFlushMode flushMode) {
this.flushMode = flushMode;
}
}
}

@ -27,7 +27,6 @@ import org.springframework.util.Assert;
*
* @author Tommy Ludwig
* @author Eddú Meléndez
* @since 1.4.0
*/
final class SessionStoreMappings {
@ -39,7 +38,7 @@ final class SessionStoreMappings {
mappings.put(StoreType.MONGO, MongoSessionConfiguration.class);
mappings.put(StoreType.REDIS, RedisSessionConfiguration.class);
mappings.put(StoreType.HAZELCAST, HazelcastSessionConfiguration.class);
mappings.put(StoreType.HASH_MAP, SimpleSessionConfiguration.class);
mappings.put(StoreType.HASH_MAP, HashMapSessionConfiguration.class);
mappings.put(StoreType.NONE, NoOpSessionConfiguration.class);
MAPPINGS = Collections.unmodifiableMap(mappings);
}
@ -63,4 +62,5 @@ final class SessionStoreMappings {
throw new IllegalStateException(
"Unknown configuration class " + configurationClassName);
}
}

@ -51,7 +51,7 @@ public enum StoreType {
HASH_MAP,
/**
* No session datastore.
* No session data-store.
*/
NONE;

@ -0,0 +1,76 @@
/*
* Copyright 2012-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.boot.autoconfigure.session;
import java.util.Collection;
import org.junit.After;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.session.SessionRepository;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Share test utilities for {@link SessionAutoConfiguration} tests.
*
* @author Stephane Nicoll
*/
public abstract class AbstractSessionAutoConfigurationTests {
protected AnnotationConfigWebApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
protected <T extends SessionRepository<?>> T validateSessionRepository(Class<T> type) {
SessionRepository cacheManager = this.context.getBean(SessionRepository.class);
assertThat(cacheManager).as("Wrong session repository type").isInstanceOf(type);
return type.cast(cacheManager);
}
protected Integer getSessionTimeout(SessionRepository<?> sessionRepository) {
return (Integer) new DirectFieldAccessor(sessionRepository)
.getPropertyValue("defaultMaxInactiveInterval");
}
protected void load(String... environment) {
load(null, environment);
}
protected void load(Collection<Class<?>> configs, String... environment) {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
if (configs != null) {
ctx.register(configs.toArray(new Class<?>[configs.size()]));
}
ctx.register(ServerPropertiesAutoConfiguration.class,
SessionAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
}

@ -0,0 +1,69 @@
/*
* Copyright 2012-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.boot.autoconfigure.session;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.redis.RedisTestServer;
import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Redis specific tests for {@link SessionAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class SessionAutoConfigurationRedisTests
extends AbstractSessionAutoConfigurationTests {
@Rule
public final RedisTestServer redis = new RedisTestServer();
@Test
public void redisSessionStore() {
load(Collections.<Class<?>>singletonList(RedisAutoConfiguration.class),
"spring.session.store-type=redis");
RedisOperationsSessionRepository repository = validateSessionRepository(
RedisOperationsSessionRepository.class);
assertThat(repository.getSessionCreatedChannelPrefix())
.isEqualTo("spring:session:event:created:");
assertThat(new DirectFieldAccessor(repository).getPropertyValue("redisFlushMode"))
.isEqualTo(RedisFlushMode.ON_SAVE);
}
@Test
public void redisSessionStoreWithCustomizations() {
load(Collections.<Class<?>>singletonList(RedisAutoConfiguration.class),
"spring.session.store-type=redis",
"spring.session.redis.namespace=foo",
"spring.session.redis.flush-mode=immediate");
RedisOperationsSessionRepository repository = validateSessionRepository(
RedisOperationsSessionRepository.class);
assertThat(repository.getSessionCreatedChannelPrefix())
.isEqualTo("spring:session:foo:event:created:");
assertThat(new DirectFieldAccessor(repository).getPropertyValue("redisFlushMode"))
.isEqualTo(RedisFlushMode.IMMEDIATE);
}
}

@ -16,76 +16,174 @@
package org.springframework.boot.autoconfigure.session;
import org.junit.After;
import org.junit.Rule;
import java.util.Arrays;
import java.util.Collections;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory;
import org.springframework.boot.redis.RedisTestServer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.ExpiringSession;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link SessionAutoConfiguration}.
*
* @author Dave Syer
* @since 1.3.0
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
public class SessionAutoConfigurationTests {
public class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurationTests {
@Rule
public RedisTestServer redis = new RedisTestServer();
@Test
public void backOffIfSessionRepositoryIsPresent() {
load(Collections.<Class<?>>singletonList(SessionRepositoryConfiguration.class),
"spring.session.store-type=mongo");
MapSessionRepository repository = validateSessionRepository(
MapSessionRepository.class);
assertThat(this.context.getBean("mySessionRepository")).isSameAs(repository);
}
@Test
public void hashMapSessionStore() {
load("spring.session.store-type=hash-map");
MapSessionRepository repository = validateSessionRepository(
MapSessionRepository.class);
assertThat(getSessionTimeout(repository)).isNull();
}
private AnnotationConfigEmbeddedWebApplicationContext context;
@Test
public void hashMapSessionStoreCustomTimeout() {
load("spring.session.store-type=hash-map",
"server.session.timeout=3000");
MapSessionRepository repository = validateSessionRepository(
MapSessionRepository.class);
assertThat(getSessionTimeout(repository)).isEqualTo(3000);
}
@After
public void close() {
if (this.context != null) {
this.context.close();
}
@Test
public void springSessionTimeoutIsNotAValidProperty() {
load("spring.session.store-type=hash-map",
"spring.session.timeout=3000");
MapSessionRepository repository = validateSessionRepository(
MapSessionRepository.class);
assertThat(getSessionTimeout(repository)).isNull();
}
@Test
public void hashMapSessionStoreIsDefault() {
load();
validateSessionRepository(MapSessionRepository.class);
}
@Test
public void jdbcSessionStore() {
load(Arrays.asList(EmbeddedDataSourceConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class),
"spring.session.store-type=jdbc");
JdbcOperationsSessionRepository repository = validateSessionRepository(
JdbcOperationsSessionRepository.class);
assertThat(new DirectFieldAccessor(repository).getPropertyValue("tableName"))
.isEqualTo("SPRING_SESSION");
}
@Test
public void jdbcSessionStoreCustomTableName() {
load(Arrays.asList(EmbeddedDataSourceConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class),
"spring.session.store-type=jdbc",
"spring.session.jdbc.table-name=FOO_BAR");
JdbcOperationsSessionRepository repository = validateSessionRepository(
JdbcOperationsSessionRepository.class);
assertThat(new DirectFieldAccessor(repository).getPropertyValue("tableName"))
.isEqualTo("FOO_BAR");
}
@Test
public void hazelcastSessionStore() {
load(Collections.<Class<?>>singletonList(HazelcastConfiguration.class),
"spring.session.store-type=hazelcast");
validateSessionRepository(MapSessionRepository.class);
}
@Test
public void hazelcastSessionStoreWithCustomizations() {
load(Collections.<Class<?>>singletonList(HazelcastSpecificMap.class),
"spring.session.store-type=hazelcast",
"spring.session.hazelcast.map-name=foo:bar:biz");
validateSessionRepository(MapSessionRepository.class);
HazelcastInstance hazelcastInstance = this.context.getBean(HazelcastInstance.class);
verify(hazelcastInstance, times(1)).getMap("foo:bar:biz");
}
@Test
public void flat() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(Config.class, ServerPropertiesAutoConfiguration.class,
RedisAutoConfiguration.class, SessionAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
ServerProperties server = this.context.getBean(ServerProperties.class);
assertThat(server).isNotNull();
public void mongoSessionStore() {
load(Arrays.asList(EmbeddedMongoAutoConfiguration.class,
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class),
"spring.session.store-type=mongo", "spring.data.mongodb.port=0");
validateSessionRepository(MongoOperationsSessionRepository.class);
}
@Test
public void hierarchy() throws Exception {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.register(RedisAutoConfiguration.class, SessionAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
parent.refresh();
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.setParent(parent);
this.context.register(Config.class, ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
ServerProperties server = this.context.getBean(ServerProperties.class);
assertThat(server).isNotNull();
public void mongoSessionStoreWithCustomizations() {
load(Arrays.asList(EmbeddedMongoAutoConfiguration.class,
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class),
"spring.session.store-type=mongo", "spring.data.mongodb.port=0",
"spring.session.mongo.collection-name=foobar");
MongoOperationsSessionRepository repository = validateSessionRepository(
MongoOperationsSessionRepository.class);
assertThat(new DirectFieldAccessor(repository).getPropertyValue("collectionName"))
.isEqualTo("foobar");
}
@Configuration
static class SessionRepositoryConfiguration {
@Bean
public SessionRepository<?> mySessionRepository() {
return new MapSessionRepository(Collections.<String, ExpiringSession>emptyMap());
}
}
@Configuration
static class HazelcastConfiguration {
@Bean
public HazelcastInstance hazelcastInstance() {
return Hazelcast.newHazelcastInstance();
}
}
@Configuration
protected static class Config {
static class HazelcastSpecificMap {
@Bean
public EmbeddedServletContainerFactory containerFactory() {
return new MockEmbeddedServletContainerFactory();
public HazelcastInstance hazelcastInstance() {
IMap map = mock(IMap.class);
HazelcastInstance mock = mock(HazelcastInstance.class);
given(mock.getMap("foo:bar:biz")).willReturn(map);
return mock;
}
}

@ -1,120 +0,0 @@
/*
* Copyright 2012-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.boot.autoconfigure.session;
import java.net.UnknownHostException;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.mongodb.MongoClient;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
public class StoreTypesConfigurationTests {
private AnnotationConfigWebApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void hashMapSessionStore() {
load("spring.session.store.type=hash-map");
MapSessionRepository sessionRepository = this.context.getBean(MapSessionRepository.class);
assertThat(sessionRepository).isNotNull();
}
@Test
public void jdbcSessionStore() {
load(new String[] {"spring.session.store.type=jdbc"}, EmbeddedDataSourceConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class);
assertThat(this.context.getBean(JdbcOperationsSessionRepository.class)).isNotNull();
}
@Test
public void hazelcastSessionStore() {
load(new String[] {"spring.session.store.type=hazelcast"}, MockHazelcastInstanceConfiguration.class);
assertThat(this.context.getBean(MapSessionRepository.class)).isNotNull();
}
@Test
public void mongoSessionStore() {
load(new String[] {"spring.session.store.type=mongo", "spring.data.mongodb.port=0"}, MockMongoConfiguration.class, MongoDataAutoConfiguration.class, EmbeddedMongoAutoConfiguration.class);
assertThat(this.context.getBean(MongoOperationsSessionRepository.class)).isNotNull();
}
private void load(String storeType) {
load(new String[] {storeType}, null);
}
private void load(String[] storeType, Class<?>... config) {
this.context = new AnnotationConfigWebApplicationContext();
for (String property : storeType) {
EnvironmentTestUtils.addEnvironment(this.context, storeType);
}
if (config != null) {
this.context.register(config);
}
this.context.register(ServerPropertiesAutoConfiguration.class,
SessionAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
}
@Configuration
static class MockHazelcastInstanceConfiguration {
@Bean
public HazelcastInstance hazelcastInstance() {
return Hazelcast.newHazelcastInstance();
}
}
@Configuration
static class MockMongoConfiguration {
@Bean
public MongoClient mongoClient(@Value("${local.mongo.port}") int port)
throws UnknownHostException {
return new MongoClient("localhost", port);
}
}
}

@ -32,9 +32,9 @@ import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAppl
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory.RegisteredFilter;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.web.filter.OrderedCharacterEncodingFilter;
import org.springframework.boot.web.filter.OrderedRequestContextFilter;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
@ -84,9 +84,10 @@ public class FilterOrderingIntegrationTests {
private void load() {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "spring.session.store.type=hash-map");
EnvironmentTestUtils.addEnvironment(this.context, "spring.session.store-type=hash-map");
this.context.register(MockEmbeddedServletContainerConfiguration.class,
TestRedisConfiguration.class, WebMvcAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
SecurityAutoConfiguration.class, SessionAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,

@ -157,7 +157,7 @@
<spring-security.version>4.0.4.RELEASE</spring-security.version>
<spring-security-jwt.version>1.0.4.RELEASE</spring-security-jwt.version>
<spring-security-oauth.version>2.0.9.RELEASE</spring-security-oauth.version>
<spring-session.version>1.2.0.RC2</spring-session.version>
<spring-session.version>1.2.0.RC3</spring-session.version>
<spring-social.version>1.1.4.RELEASE</spring-social.version>
<spring-social-facebook.version>2.0.3.RELEASE</spring-social-facebook.version>
<spring-social-linkedin.version>1.0.2.RELEASE</spring-social-linkedin.version>

@ -356,8 +356,13 @@ content into your application; rather pick only the properties that you need.
spring.resources.chain.strategy.fixed.version= # Version string to use for the Version Strategy.
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ # Locations of static resources.
# SPRING SESSION ({sc-spring-boot-autoconfigure}/session/SocialWebAutoConfiguration.{sc-ext}[SessionAutoConfiguration])
spring.session.store.store-type= # Session store type
# SPRING SESSION ({sc-spring-boot-autoconfigure}/session/SessionProperties.{sc-ext}[SessionProperties])
spring.session.hazelcast.map-name=spring:session:sessions # Name of the map used to store sessions.
spring.session.jdbc.table-name=SPRING_SESSION # Name of database table used to store sessions.
spring.session.mongo.collection-name=sessions # Collection name used to store sessions.
spring.session.redis.flush-mode= # Flush mode for the Redis sessions.
spring.session.redis.namespace= # Namespace for keys used to store sessions.
spring.session.store-type= # Session store type, auto-detected according to the environment by default.
# SPRING SOCIAL ({sc-spring-boot-autoconfigure}/social/SocialWebAutoConfiguration.{sc-ext}[SocialWebAutoConfiguration])
spring.social.auto-connection-views=false # Enable the connection status view for supported providers.

@ -4392,21 +4392,24 @@ class for more details.
[[boot-features-session]]
== Spring Session
Spring Session provides support for managing a user's session information. If you are
writing a web application and Spring Session and one the following providers:
Spring Boot provides Spring Session auto-configuration for a wide range of stores. If
Spring Session is available and you haven't defined a bean of type `SessionRepository`,
Spring Boot tries to detect the following session stores (in this order):
* JDBC
* MongoDB
* Redis
* Hazelcast
* Spring Data Mongo
* Spring Data Redis
* HashMap
on the classpath, Spring Boot will auto-configure Spring Session through its
`@EnableJdbcHttpSession`, `@EnableHazelcastHttpSession`, `@EnableMongoHttpSession`,
`@EnableRedisHttpSession`. Session data will be stored in the provider and the session timeout
can be configured using the `server.session.timeout` property.
It is also possible to _force_ the store to use via the `spring.session.store-type`
property. Each store have specific additional settings. For instance it is possible
to customize the name of the table for the jdbc store:
It is also possible to _force_ the session provider to use via the `spring.session.store.type`
property.
[source,properties,indent=0]
----
spring.session.jdbc.table-name=SESSIONS
----

Loading…
Cancel
Save