Add auto-configuration for Spring Session's reactive support
Closes gh-9850
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.session;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
@@ -27,13 +28,20 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* General condition used with all session configuration classes.
|
||||
* Base class for Servlet and reactive session conditions.
|
||||
*
|
||||
* @author Tommy Ludwig
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SessionCondition extends SpringBootCondition {
|
||||
class AbstractSessionCondition extends SpringBootCondition {
|
||||
|
||||
private final WebApplicationType webApplicationType;
|
||||
|
||||
protected AbstractSessionCondition(WebApplicationType webApplicationType) {
|
||||
this.webApplicationType = webApplicationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
@@ -41,8 +49,8 @@ class SessionCondition extends SpringBootCondition {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("Session Condition");
|
||||
Environment environment = context.getEnvironment();
|
||||
StoreType required = SessionStoreMappings
|
||||
.getType(((AnnotationMetadata) metadata).getClassName());
|
||||
StoreType required = SessionStoreMappings.getType(this.webApplicationType,
|
||||
((AnnotationMetadata) metadata).getClassName());
|
||||
if (!environment.containsProperty("spring.session.store-type")) {
|
||||
return ConditionOutcome.match(message.didNotFind("property", "properties")
|
||||
.items(ConditionMessage.Style.QUOTE, "spring.session.store-type"));
|
||||
@@ -41,7 +41,7 @@ import org.springframework.session.hazelcast.config.annotation.web.http.Hazelcas
|
||||
@ConditionalOnClass(HazelcastSessionRepository.class)
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@ConditionalOnBean(HazelcastInstance.class)
|
||||
@Conditional(SessionCondition.class)
|
||||
@Conditional(ServletSessionCondition.class)
|
||||
@EnableConfigurationProperties(HazelcastSessionProperties.class)
|
||||
class HazelcastSessionConfiguration {
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessi
|
||||
@ConditionalOnClass({ JdbcTemplate.class, JdbcOperationsSessionRepository.class })
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@Conditional(SessionCondition.class)
|
||||
@Conditional(ServletSessionCondition.class)
|
||||
@EnableConfigurationProperties(JdbcSessionProperties.class)
|
||||
class JdbcSessionConfiguration {
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.reactive.ReactiveMongoWebSessionConfiguration;
|
||||
|
||||
/**
|
||||
* Mongo-backed reactive session configuration.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(ReactiveMongoWebSessionConfiguration.class)
|
||||
@ConditionalOnMissingBean(ReactiveSessionRepository.class)
|
||||
@ConditionalOnBean(ReactiveMongoOperations.class)
|
||||
@Conditional(ReactiveSessionCondition.class)
|
||||
@EnableConfigurationProperties(MongoSessionProperties.class)
|
||||
class MongoReactiveSessionConfiguration {
|
||||
|
||||
@Configuration
|
||||
static class SpringBootReactiveMongoWebSessionConfiguration
|
||||
extends ReactiveMongoWebSessionConfiguration {
|
||||
|
||||
@Autowired
|
||||
public void customize(SessionProperties sessionProperties,
|
||||
MongoSessionProperties mongoSessionProperties) {
|
||||
Integer timeout = sessionProperties.getTimeout();
|
||||
if (timeout != null) {
|
||||
setMaxInactiveIntervalInSeconds(timeout);
|
||||
}
|
||||
setCollectionName(mongoSessionProperties.getCollectionName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import org.springframework.session.data.mongo.config.annotation.web.http.MongoHt
|
||||
@ConditionalOnClass(MongoHttpSessionConfiguration.class)
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@ConditionalOnBean(MongoOperations.class)
|
||||
@Conditional(SessionCondition.class)
|
||||
@Conditional(ServletSessionCondition.class)
|
||||
@EnableConfigurationProperties(MongoSessionProperties.class)
|
||||
class MongoSessionConfiguration {
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
|
||||
/**
|
||||
* No-op session configuration used to disable Spring Session using the environment.
|
||||
*
|
||||
* @author Tommy Ludwig
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(ReactiveSessionRepository.class)
|
||||
@Conditional(ReactiveSessionCondition.class)
|
||||
class NoOpReactiveSessionConfiguration {
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import org.springframework.session.SessionRepository;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@Conditional(SessionCondition.class)
|
||||
@Conditional(ServletSessionCondition.class)
|
||||
class NoOpSessionConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 org.springframework.boot.WebApplicationType;
|
||||
|
||||
/**
|
||||
* General condition used with all reactive session configuration classes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ReactiveSessionCondition extends AbstractSessionCondition {
|
||||
|
||||
ReactiveSessionCondition() {
|
||||
super(WebApplicationType.REACTIVE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.web.server.RedisWebSessionConfiguration;
|
||||
|
||||
/**
|
||||
* Redis-backed reactive session configuration.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ ReactiveRedisConnectionFactory.class,
|
||||
ReactiveRedisOperationsSessionRepository.class })
|
||||
@ConditionalOnMissingBean(ReactiveSessionRepository.class)
|
||||
@ConditionalOnBean(ReactiveRedisConnectionFactory.class)
|
||||
@Conditional(ReactiveSessionCondition.class)
|
||||
@EnableConfigurationProperties(RedisSessionProperties.class)
|
||||
class RedisReactiveSessionConfiguration {
|
||||
|
||||
@Configuration
|
||||
static class SpringBootRedisWebSessionConfiguration
|
||||
extends RedisWebSessionConfiguration {
|
||||
|
||||
private SessionProperties sessionProperties;
|
||||
|
||||
@Autowired
|
||||
public void customize(SessionProperties sessionProperties,
|
||||
RedisSessionProperties redisSessionProperties) {
|
||||
this.sessionProperties = sessionProperties;
|
||||
Integer timeout = this.sessionProperties.getTimeout();
|
||||
if (timeout != null) {
|
||||
setMaxInactiveIntervalInSeconds(timeout);
|
||||
}
|
||||
setRedisNamespace(redisSessionProperties.getNamespace());
|
||||
setRedisFlushMode(redisSessionProperties.getFlushMode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import org.springframework.session.data.redis.config.annotation.web.http.RedisHt
|
||||
@ConditionalOnClass({ RedisTemplate.class, RedisOperationsSessionRepository.class })
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@ConditionalOnBean(RedisConnectionFactory.class)
|
||||
@Conditional(SessionCondition.class)
|
||||
@Conditional(ServletSessionCondition.class)
|
||||
@EnableConfigurationProperties(RedisSessionProperties.class)
|
||||
class RedisSessionConfiguration {
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 org.springframework.boot.WebApplicationType;
|
||||
|
||||
/**
|
||||
* General condition used with all servlet session configuration classes.
|
||||
*
|
||||
* @author Tommy Ludwig
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ServletSessionCondition extends AbstractSessionCondition {
|
||||
|
||||
ServletSessionCondition() {
|
||||
super(WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,11 +17,13 @@
|
||||
package org.springframework.boot.autoconfigure.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -43,6 +45,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
@@ -69,77 +72,116 @@ public class SessionAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@Import({ SessionRepositoryConfiguration.class, SessionRepositoryValidator.class,
|
||||
@Import({ ServletSessionRepositoryValidator.class,
|
||||
SessionRepositoryFilterConfiguration.class })
|
||||
static class ServletSessionConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@Import({ ServletSessionRepositoryImplementationValidator.class,
|
||||
ServletSessionConfigurationImportSelector.class })
|
||||
static class ServletSessionRepositoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@Import(ReactiveSessionRepositoryValidator.class)
|
||||
static class ReactiveSessionConfiguration {
|
||||
|
||||
}
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(ReactiveSessionRepository.class)
|
||||
@Import({ ReactiveSessionRepositoryImplementationValidator.class,
|
||||
ReactiveSessionConfigurationImportSelector.class })
|
||||
static class ReactiveSessionRepositoryConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(SessionRepository.class)
|
||||
@Import({ SessionRepositoryImplementationValidator.class,
|
||||
SessionConfigurationImportSelector.class })
|
||||
static class SessionRepositoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to add {@link StoreType} configuration classes.
|
||||
*/
|
||||
static class SessionConfigurationImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
StoreType[] types = StoreType.values();
|
||||
String[] imports = new String[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
imports[i] = SessionStoreMappings.getConfigurationClass(types[i]);
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean used to validate that only one supported implementation is available in the
|
||||
* classpath when the store-type property is not set.
|
||||
* {@link ImportSelector} base class to add {@link StoreType} configuration classes.
|
||||
*/
|
||||
static class SessionRepositoryImplementationValidator {
|
||||
static abstract class SessionConfigurationImportSelector implements ImportSelector {
|
||||
|
||||
protected final String[] selectImports(AnnotationMetadata importingClassMetadata,
|
||||
WebApplicationType webApplicationType) {
|
||||
List<String> imports = new ArrayList<>();
|
||||
StoreType[] types = StoreType.values();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
imports.add(SessionStoreMappings.getConfigurationClass(webApplicationType,
|
||||
types[i]));
|
||||
}
|
||||
return imports.toArray(new String[imports.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to add {@link StoreType} configuration classes for reactive
|
||||
* web applications.
|
||||
*/
|
||||
static class ReactiveSessionConfigurationImportSelector
|
||||
extends SessionConfigurationImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
return super.selectImports(importingClassMetadata,
|
||||
WebApplicationType.REACTIVE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to add {@link StoreType} configuration classes for Servlet
|
||||
* web applications.
|
||||
*/
|
||||
static class ServletSessionConfigurationImportSelector
|
||||
extends SessionConfigurationImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
return super.selectImports(importingClassMetadata,
|
||||
WebApplicationType.SERVLET);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for beans used to validate that only one supported implementation is
|
||||
* available in the classpath when the store-type property is not set.
|
||||
*/
|
||||
static class AbstractSessionRepositoryImplementationValidator {
|
||||
|
||||
private final List<String> candidates;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final SessionProperties sessionProperties;
|
||||
|
||||
SessionRepositoryImplementationValidator(ApplicationContext applicationContext,
|
||||
SessionProperties sessionProperties) {
|
||||
AbstractSessionRepositoryImplementationValidator(
|
||||
ApplicationContext applicationContext,
|
||||
SessionProperties sessionProperties, List<String> candidates) {
|
||||
this.classLoader = applicationContext.getClassLoader();
|
||||
this.sessionProperties = sessionProperties;
|
||||
this.candidates = candidates;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void checkAvailableImplementations() {
|
||||
List<Class<?>> candidates = new ArrayList<>();
|
||||
addCandidate(candidates,
|
||||
"org.springframework.session.hazelcast.HazelcastSessionRepository");
|
||||
addCandidate(candidates,
|
||||
"org.springframework.session.jdbc.JdbcOperationsSessionRepository");
|
||||
addCandidate(candidates,
|
||||
"org.springframework.session.data.mongo.MongoOperationsSessionRepository");
|
||||
addCandidate(candidates,
|
||||
"org.springframework.session.data.redis.RedisOperationsSessionRepository");
|
||||
List<Class<?>> availableCandidates = new ArrayList<>();
|
||||
for (String candidate : this.candidates) {
|
||||
addCandidateIfAvailable(availableCandidates, candidate);
|
||||
}
|
||||
StoreType storeType = this.sessionProperties.getStoreType();
|
||||
if (candidates.size() > 1 && storeType == null) {
|
||||
throw new NonUniqueSessionRepositoryException(candidates);
|
||||
if (availableCandidates.size() > 1 && storeType == null) {
|
||||
throw new NonUniqueSessionRepositoryException(availableCandidates);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCandidate(List<Class<?>> candidates, String type) {
|
||||
private void addCandidateIfAvailable(List<Class<?>> candidates, String type) {
|
||||
try {
|
||||
Class<?> candidate = this.classLoader.loadClass(type);
|
||||
if (candidate != null) {
|
||||
@@ -153,17 +195,52 @@ public class SessionAutoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean used to validate that a {@link SessionRepository} exists and provide a
|
||||
* meaningful message if that's not the case.
|
||||
* Bean used to validate that only one supported implementation is available in the
|
||||
* classpath when the store-type property is not set.
|
||||
*/
|
||||
static class SessionRepositoryValidator {
|
||||
static class ServletSessionRepositoryImplementationValidator
|
||||
extends AbstractSessionRepositoryImplementationValidator {
|
||||
|
||||
private SessionProperties sessionProperties;
|
||||
ServletSessionRepositoryImplementationValidator(
|
||||
ApplicationContext applicationContext,
|
||||
SessionProperties sessionProperties) {
|
||||
super(applicationContext, sessionProperties, Arrays.asList(
|
||||
"org.springframework.session.hazelcast.HazelcastSessionRepository",
|
||||
"org.springframework.session.jdbc.JdbcOperationsSessionRepository",
|
||||
"org.springframework.session.data.mongo.MongoOperationsSessionRepository",
|
||||
"org.springframework.session.data.redis.RedisOperationsSessionRepository"));
|
||||
}
|
||||
|
||||
private ObjectProvider<SessionRepository<?>> sessionRepositoryProvider;
|
||||
}
|
||||
|
||||
SessionRepositoryValidator(SessionProperties sessionProperties,
|
||||
ObjectProvider<SessionRepository<?>> sessionRepositoryProvider) {
|
||||
/**
|
||||
* Bean used to validate that only one supported implementation is available in the
|
||||
* classpath when the store-type property is not set.
|
||||
*/
|
||||
static class ReactiveSessionRepositoryImplementationValidator
|
||||
extends AbstractSessionRepositoryImplementationValidator {
|
||||
|
||||
ReactiveSessionRepositoryImplementationValidator(
|
||||
ApplicationContext applicationContext,
|
||||
SessionProperties sessionProperties) {
|
||||
super(applicationContext, sessionProperties, Arrays.asList(
|
||||
"org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository",
|
||||
"org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for validating that a (reactive) session repository bean exists.
|
||||
*/
|
||||
static class AbstractSessionRepositoryValidator {
|
||||
|
||||
private final SessionProperties sessionProperties;
|
||||
|
||||
private final ObjectProvider<?> sessionRepositoryProvider;
|
||||
|
||||
protected AbstractSessionRepositoryValidator(SessionProperties sessionProperties,
|
||||
ObjectProvider<?> sessionRepositoryProvider) {
|
||||
this.sessionProperties = sessionProperties;
|
||||
this.sessionRepositoryProvider = sessionRepositoryProvider;
|
||||
}
|
||||
@@ -184,4 +261,32 @@ public class SessionAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean used to validate that a {@link SessionRepository} exists and provide a
|
||||
* meaningful message if that's not the case.
|
||||
*/
|
||||
static class ServletSessionRepositoryValidator
|
||||
extends AbstractSessionRepositoryValidator {
|
||||
|
||||
ServletSessionRepositoryValidator(SessionProperties sessionProperties,
|
||||
ObjectProvider<SessionRepository<?>> sessionRepositoryProvider) {
|
||||
super(sessionProperties, sessionRepositoryProvider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean used to validate that a {@link SessionRepository} exists and provide a
|
||||
* meaningful message if that's not the case.
|
||||
*/
|
||||
static class ReactiveSessionRepositoryValidator
|
||||
extends AbstractSessionRepositoryValidator {
|
||||
|
||||
ReactiveSessionRepositoryValidator(SessionProperties sessionProperties,
|
||||
ObjectProvider<ReactiveSessionRepository<?>> sessionRepositoryProvider) {
|
||||
super(sessionProperties, sessionRepositoryProvider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -30,32 +31,62 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
final class SessionStoreMappings {
|
||||
|
||||
private static final Map<StoreType, Class<?>> MAPPINGS;
|
||||
private static final Map<StoreType, Map<WebApplicationType, Class<?>>> MAPPINGS;
|
||||
|
||||
static {
|
||||
Map<StoreType, Class<?>> mappings = new HashMap<>();
|
||||
mappings.put(StoreType.REDIS, RedisSessionConfiguration.class);
|
||||
mappings.put(StoreType.MONGODB, MongoSessionConfiguration.class);
|
||||
mappings.put(StoreType.JDBC, JdbcSessionConfiguration.class);
|
||||
mappings.put(StoreType.HAZELCAST, HazelcastSessionConfiguration.class);
|
||||
mappings.put(StoreType.NONE, NoOpSessionConfiguration.class);
|
||||
Map<StoreType, Map<WebApplicationType, Class<?>>> mappings = new HashMap<>();
|
||||
mappings.put(StoreType.REDIS, createMapping(RedisSessionConfiguration.class,
|
||||
RedisReactiveSessionConfiguration.class));
|
||||
mappings.put(StoreType.MONGODB, createMapping(MongoSessionConfiguration.class,
|
||||
MongoReactiveSessionConfiguration.class));
|
||||
mappings.put(StoreType.JDBC, createMapping(JdbcSessionConfiguration.class));
|
||||
mappings.put(StoreType.HAZELCAST,
|
||||
createMapping(HazelcastSessionConfiguration.class));
|
||||
mappings.put(StoreType.NONE, createMapping(NoOpSessionConfiguration.class,
|
||||
NoOpReactiveSessionConfiguration.class));
|
||||
MAPPINGS = Collections.unmodifiableMap(mappings);
|
||||
}
|
||||
|
||||
static Map<WebApplicationType, Class<?>> createMapping(
|
||||
Class<?> servletConfiguration) {
|
||||
return createMapping(servletConfiguration, null);
|
||||
}
|
||||
|
||||
static Map<WebApplicationType, Class<?>> createMapping(Class<?> servletConfiguration,
|
||||
Class<?> reactiveConfiguration) {
|
||||
Map<WebApplicationType, Class<?>> mapping = new HashMap<>();
|
||||
mapping.put(WebApplicationType.SERVLET, servletConfiguration);
|
||||
if (reactiveConfiguration != null) {
|
||||
mapping.put(WebApplicationType.REACTIVE, reactiveConfiguration);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private SessionStoreMappings() {
|
||||
}
|
||||
|
||||
static String getConfigurationClass(StoreType sessionStoreType) {
|
||||
Class<?> configurationClass = MAPPINGS.get(sessionStoreType);
|
||||
Assert.state(configurationClass != null,
|
||||
static String getConfigurationClass(WebApplicationType webApplicationType,
|
||||
StoreType sessionStoreType) {
|
||||
Map<WebApplicationType, Class<?>> configurationClasses = MAPPINGS
|
||||
.get(sessionStoreType);
|
||||
Assert.state(configurationClasses != null,
|
||||
() -> "Unknown session store type " + sessionStoreType);
|
||||
Class<?> configurationClass = configurationClasses.get(webApplicationType);
|
||||
if (configurationClass == null) {
|
||||
return null;
|
||||
}
|
||||
return configurationClass.getName();
|
||||
}
|
||||
|
||||
static StoreType getType(String configurationClassName) {
|
||||
for (Map.Entry<StoreType, Class<?>> entry : MAPPINGS.entrySet()) {
|
||||
if (entry.getValue().getName().equals(configurationClassName)) {
|
||||
return entry.getKey();
|
||||
static StoreType getType(WebApplicationType webApplicationType,
|
||||
String configurationClassName) {
|
||||
for (Map.Entry<StoreType, Map<WebApplicationType, Class<?>>> storeEntry : MAPPINGS
|
||||
.entrySet()) {
|
||||
for (Map.Entry<WebApplicationType, Class<?>> entry : storeEntry.getValue()
|
||||
.entrySet()) {
|
||||
if (entry.getValue().getName().equals(configurationClassName)) {
|
||||
return storeEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.reactive;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -30,14 +29,12 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HttpHandler}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -58,16 +55,9 @@ public class HttpHandlerAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler(
|
||||
ObjectProvider<WebSessionManager> webSessionManagerProvider) {
|
||||
WebHttpHandlerBuilder builder = WebHttpHandlerBuilder
|
||||
.applicationContext(this.applicationContext);
|
||||
WebSessionManager webSessionManager = webSessionManagerProvider
|
||||
.getIfAvailable();
|
||||
if (webSessionManager != null) {
|
||||
builder.sessionManager(webSessionManager);
|
||||
}
|
||||
return builder.build();
|
||||
public HttpHandler httpHandler() {
|
||||
return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package org.springframework.boot.autoconfigure.session;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -42,4 +44,18 @@ public abstract class AbstractSessionAutoConfigurationTests {
|
||||
.getPropertyValue("defaultMaxInactiveInterval");
|
||||
}
|
||||
|
||||
protected <T extends ReactiveSessionRepository<?>> T validateSessionRepository(
|
||||
AssertableReactiveWebApplicationContext context, Class<T> type) {
|
||||
assertThat(context).hasSingleBean(ReactiveSessionRepository.class);
|
||||
ReactiveSessionRepository<?> repository = context
|
||||
.getBean(ReactiveSessionRepository.class);
|
||||
assertThat(repository).as("Wrong session repository type").isInstanceOf(type);
|
||||
return type.cast(repository);
|
||||
}
|
||||
|
||||
protected Integer getSessionTimeout(ReactiveSessionRepository<?> sessionRepository) {
|
||||
return (Integer) new DirectFieldAccessor(sessionRepository)
|
||||
.getPropertyValue("defaultMaxInactiveInterval");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
|
||||
import org.springframework.boot.test.context.HideClassesClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Mongo-specific tests for {@link SessionAutoConfiguration}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ReactiveSessionAutoConfigurationMongoTests
|
||||
extends AbstractSessionAutoConfigurationTests {
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void defaultConfig() {
|
||||
this.contextRunner.withPropertyValues("spring.session.store-type=mongodb")
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
EmbeddedMongoAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoReactiveAutoConfiguration.class,
|
||||
MongoReactiveDataAutoConfiguration.class))
|
||||
.run(validateSpringSessionUsesMongo("sessions"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConfigWithUniqueStoreImplementation() {
|
||||
this.contextRunner
|
||||
.withClassLoader(new HideClassesClassLoader(
|
||||
ReactiveRedisOperationsSessionRepository.class))
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
EmbeddedMongoAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoReactiveAutoConfiguration.class,
|
||||
MongoReactiveDataAutoConfiguration.class))
|
||||
.run(validateSpringSessionUsesMongo("sessions"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoSessionStoreWithCustomizations() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
EmbeddedMongoAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoReactiveAutoConfiguration.class,
|
||||
MongoReactiveDataAutoConfiguration.class))
|
||||
.withPropertyValues("spring.session.store-type=mongodb",
|
||||
"spring.session.mongodb.collection-name=foo")
|
||||
.run(validateSpringSessionUsesMongo("foo"));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableReactiveWebApplicationContext> validateSpringSessionUsesMongo(
|
||||
String collectionName) {
|
||||
return (context) -> {
|
||||
ReactiveMongoOperationsSessionRepository repository = validateSessionRepository(
|
||||
context, ReactiveMongoOperationsSessionRepository.class);
|
||||
assertThat(new DirectFieldAccessor(repository)
|
||||
.getPropertyValue("collectionName")).isEqualTo(collectionName);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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 org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportMessage;
|
||||
import org.springframework.boot.test.context.HideClassesClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.rule.RedisTestServer;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Reactive Redis-specific tests for {@link SessionAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ReactiveSessionAutoConfigurationRedisTests
|
||||
extends AbstractSessionAutoConfigurationTests {
|
||||
|
||||
@Rule
|
||||
public final RedisTestServer redis = new RedisTestServer();
|
||||
|
||||
protected final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void defaultConfig() {
|
||||
this.contextRunner.withPropertyValues("spring.session.store-type=redis")
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
RedisReactiveAutoConfiguration.class))
|
||||
.run(validateSpringSessionUsesRedis(RedisFlushMode.ON_SAVE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConfigWithUniqueStoreImplementation() {
|
||||
this.contextRunner
|
||||
.withClassLoader(new HideClassesClassLoader(
|
||||
ReactiveMongoOperationsSessionRepository.class))
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
RedisReactiveAutoConfiguration.class))
|
||||
.run(validateSpringSessionUsesRedis(RedisFlushMode.ON_SAVE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisSessionStoreWithCustomizations() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
RedisReactiveAutoConfiguration.class))
|
||||
.withPropertyValues("spring.session.store-type=redis",
|
||||
"spring.session.redis.namespace=foo",
|
||||
"spring.session.redis.flush-mode=immediate")
|
||||
.run(validateSpringSessionUsesRedis(RedisFlushMode.IMMEDIATE));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableReactiveWebApplicationContext> validateSpringSessionUsesRedis(
|
||||
RedisFlushMode flushMode) {
|
||||
return (context) -> {
|
||||
System.out.println(new ConditionEvaluationReportMessage(
|
||||
context.getBean(ConditionEvaluationReport.class)));
|
||||
ReactiveRedisOperationsSessionRepository repository = validateSessionRepository(
|
||||
context, ReactiveRedisOperationsSessionRepository.class);
|
||||
assertThat(new DirectFieldAccessor(repository)
|
||||
.getPropertyValue("redisFlushMode")).isEqualTo(flushMode);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -157,7 +157,7 @@
|
||||
<spring-retry.version>1.2.1.RELEASE</spring-retry.version>
|
||||
<spring-security.version>5.0.0.BUILD-SNAPSHOT</spring-security.version>
|
||||
<spring-session.version>2.0.0.BUILD-SNAPSHOT</spring-session.version>
|
||||
<spring-session-data-mongodb.version>2.0.0.M4</spring-session-data-mongodb.version>
|
||||
<spring-session-data-mongodb.version>2.0.0.BUILD-SNAPSHOT</spring-session-data-mongodb.version>
|
||||
<spring-social.version>2.0.0.M4</spring-social.version>
|
||||
<spring-social-facebook.version>3.0.0.M3</spring-social-facebook.version>
|
||||
<spring-social-linkedin.version>2.0.0.M3</spring-social-linkedin.version>
|
||||
|
||||
@@ -5506,13 +5506,19 @@ classes for more details.
|
||||
|
||||
[[boot-features-session]]
|
||||
== Spring Session
|
||||
Spring Boot provides Spring Session auto-configuration for a wide range of stores:
|
||||
Spring Boot provides Spring Session auto-configuration for a wide range of stores. When
|
||||
building a Servlet web application, the following stores can be auto-configured:
|
||||
|
||||
* JDBC
|
||||
* Redis
|
||||
* Hazelcast
|
||||
* MongoDB
|
||||
|
||||
When building a reactive web applicaiton, the following stores can be auto-configured:
|
||||
|
||||
* Redis
|
||||
* MongoDB
|
||||
|
||||
If Spring Session is available, you must choose the
|
||||
{sc-spring-boot-autoconfigure}/session/StoreType.{sc-ext}[`StoreType`] that you wish to
|
||||
use to store the sessions. For instance to use JDBC as backend store, you'd configure
|
||||
|
||||
Reference in New Issue
Block a user