Relax use of spring.session.store-type

This commit makes the "spring.session.store-type" property optional,
adding an additional check when it is not present that validates only
one supported implementation is available on the classpath.

As Spring Session has been modularized, the chance that multiple
implementations are available on the classpath are lower. When only
one implementation is present, we attempt to auto-configure it. When
more than one implementation is present and no session store is
configured, a NonUniqueSessionRepositoryException is thrown.

Closes gh-9863
This commit is contained in:
Stephane Nicoll 2017-09-13 11:20:06 +02:00
parent de47827eb4
commit 4670cc7795
14 changed files with 493 additions and 44 deletions

View File

@ -0,0 +1,48 @@
/*
* 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 java.util.Collections;
import java.util.List;
import org.springframework.session.SessionRepository;
import org.springframework.util.ObjectUtils;
/**
* Exception thrown when multiple {@link SessionRepository} implementations are
* available with no way to know which implementation should be used.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class NonUniqueSessionRepositoryException extends RuntimeException {
private final List<Class<? extends SessionRepository>> availableCandidates;
public NonUniqueSessionRepositoryException(
List<Class<? extends SessionRepository>> availableCandidates) {
super("Multiple session repository candidates are available, set the "
+ "'spring.session.store-type' property accordingly");
this.availableCandidates = (!ObjectUtils.isEmpty(availableCandidates)
? availableCandidates : Collections.emptyList());
}
public List<Class<? extends SessionRepository>> getAvailableCandidates() {
return this.availableCandidates;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.session.SessionRepository;
/**
* A {@link AbstractFailureAnalyzer} for {@link NonUniqueSessionRepositoryException}.
*
* @author Stephane Nicoll
*/
class NonUniqueSessionRepositoryFailureAnalyzer
extends AbstractFailureAnalyzer<NonUniqueSessionRepositoryException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
NonUniqueSessionRepositoryException cause) {
StringBuilder message = new StringBuilder();
message.append(String.format("Multiple Spring Session store implementations are "
+ "available on the classpath:%n"));
for (Class<? extends SessionRepository> candidate : cause.getAvailableCandidates()) {
message.append(String.format(" - %s%n", candidate.getName()));
}
StringBuilder action = new StringBuilder();
action.append(String.format("Consider any of the following:%n"));
action.append(String.format(" - Define the `spring.session.store-type` "
+ "property to the store you want to use%n"));
action.append(String.format(" - Review your classpath and remove the unwanted "
+ "store implementation(s)%n"));
return new FailureAnalysis(message.toString(), action.toString(), cause);
}
}

View File

@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.session;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.ObjectProvider;
@ -32,6 +35,7 @@ import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration.SessionRepositoryConfiguration;
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration.SessionRepositoryValidator;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
@ -61,7 +65,8 @@ public class SessionAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(SessionRepository.class)
@Import(SessionConfigurationImportSelector.class)
@Import({ SessionRepositoryImplementationValidator.class,
SessionConfigurationImportSelector.class })
static class SessionRepositoryConfiguration {
}
@ -83,6 +88,51 @@ public class SessionAutoConfiguration {
}
/**
* Bean used to validate that only one supported implementation is available in the
* classpath when the store-type property is not set.
*/
static class SessionRepositoryImplementationValidator {
private final ClassLoader classLoader;
private final SessionProperties sessionProperties;
SessionRepositoryImplementationValidator(ApplicationContext applicationContext,
SessionProperties sessionProperties) {
this.classLoader = applicationContext.getClassLoader();
this.sessionProperties = sessionProperties;
}
@PostConstruct
public void checkAvailableImplementations() {
List<Class<? extends SessionRepository>> candidates = new ArrayList<>();
addCandidate(candidates,
"org.springframework.session.hazelcast.HazelcastSessionRepository");
addCandidate(candidates,
"org.springframework.session.jdbc.JdbcOperationsSessionRepository");
addCandidate(candidates,
"org.springframework.session.data.redis.RedisOperationsSessionRepository");
StoreType storeType = this.sessionProperties.getStoreType();
if (candidates.size() > 1 && storeType == null) {
throw new NonUniqueSessionRepositoryException(candidates);
}
}
private void addCandidate(
List<Class<? extends SessionRepository>> candidates, String fqn) {
try {
Class<? extends SessionRepository> candidate = (Class<? extends SessionRepository>) this.classLoader.loadClass(fqn);
if (candidate != null) {
candidates.add(candidate);
}
}
catch (Throwable ex) {
// Ignore
}
}
}
/**
* Bean used to validate that a {@link SessionRepository} exists and provide a
* meaningful message if that's not the case.
@ -105,12 +155,11 @@ public class SessionAutoConfiguration {
if (storeType != StoreType.NONE
&& this.sessionRepositoryProvider.getIfAvailable() == null) {
if (storeType != null) {
throw new IllegalArgumentException("No session repository could be "
+ "auto-configured, check your configuration (session store "
+ "type is '" + storeType.name().toLowerCase() + "')");
throw new SessionRepositoryUnavailableException("No session "
+ "repository could be auto-configured, check your "
+ "configuration (session store type is '"
+ storeType.name().toLowerCase() + "')", storeType);
}
throw new IllegalArgumentException("No Spring Session store is "
+ "configured: set the 'spring.session.store-type' property");
}
}

View File

@ -44,8 +44,8 @@ class SessionCondition extends SpringBootCondition {
StoreType required = SessionStoreMappings
.getType(((AnnotationMetadata) metadata).getClassName());
if (!environment.containsProperty("spring.session.store-type")) {
return ConditionOutcome.noMatch(
message.didNotFind("spring.session.store-type property").atAll());
return ConditionOutcome.match(message.didNotFind("property", "properties")
.items(ConditionMessage.Style.QUOTE, "spring.session.store-type"));
}
try {
Binder binder = Binder.get(environment);

View File

@ -0,0 +1,40 @@
/*
* 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.session.SessionRepository;
/**
* Exception thrown when no {@link SessionRepository} is available.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class SessionRepositoryUnavailableException extends RuntimeException {
private final StoreType storeType;
public SessionRepositoryUnavailableException(String message, StoreType storeType) {
super(message);
this.storeType = storeType;
}
public StoreType getStoreType() {
return this.storeType;
}
}

View File

@ -130,7 +130,8 @@ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\

View File

@ -0,0 +1,64 @@
/*
* 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 java.util.Arrays;
import org.junit.Test;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.FailureAnalyzer;
import org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter;
import org.springframework.session.SessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link NonUniqueSessionRepositoryFailureAnalyzer}.
*
* @author Stephane Nicoll
*/
public class NonUniqueSessionRepositoryFailureAnalyzerTests {
private final FailureAnalyzer analyzer = new NonUniqueSessionRepositoryFailureAnalyzer();
@Test
public void failureAnalysisWithMultipleCandidates() {
FailureAnalysis analysis = analyzeFailure(createFailure(
JdbcOperationsSessionRepository.class, HazelcastSessionRepository.class));
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains(
JdbcOperationsSessionRepository.class.getName(),
HazelcastSessionRepository.class.getName());
assertThat(analysis.getAction()).contains("spring.session.store-type");
}
private Exception createFailure(Class<? extends SessionRepository>... candidates) {
return new NonUniqueSessionRepositoryException(Arrays.asList(candidates));
}
private FailureAnalysis analyzeFailure(Exception failure) {
FailureAnalysis analysis = this.analyzer.analyze(failure);
if (analysis != null) {
new LoggingFailureAnalysisReporter().report(analysis);
}
return analysis;
}
}

View File

@ -22,11 +22,15 @@ import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.HideClassesClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastFlushMode;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@ -48,13 +52,24 @@ public class SessionAutoConfigurationHazelcastTests
@Test
public void defaultConfig() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast")
.run((context) -> {
validateSessionRepository(context, HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = context
.getBean(HazelcastInstance.class);
verify(hazelcastInstance, times(1)).getMap("spring:session:sessions");
});
this.contextRunner
.withPropertyValues("spring.session.store-type=hazelcast")
.run(this::validateDefaultConfig);
}
@Test
public void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner.withClassLoader(new HideClassesClassLoader(
JdbcOperationsSessionRepository.class,
RedisOperationsSessionRepository.class)).run(
this::validateDefaultConfig);
}
private void validateDefaultConfig(AssertableWebApplicationContext context) {
validateSessionRepository(context, HazelcastSessionRepository.class);
HazelcastInstance hazelcastInstance = context
.getBean(HazelcastInstance.class);
verify(hazelcastInstance, times(1)).getMap("spring:session:sessions");
}
@Test
@ -80,7 +95,7 @@ public class SessionAutoConfigurationHazelcastTests
context, HazelcastSessionRepository.class);
assertThat(new DirectFieldAccessor(repository)
.getPropertyValue("hazelcastFlushMode"))
.isEqualTo(HazelcastFlushMode.IMMEDIATE);
.isEqualTo(HazelcastFlushMode.IMMEDIATE);
});
}

View File

@ -0,0 +1,101 @@
/*
* 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 com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
/**
* Integration tests for {@link SessionAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class SessionAutoConfigurationIntegrationTests
extends AbstractSessionAutoConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
SessionAutoConfiguration.class))
.withPropertyValues("spring.datasource.generate-unique-name=true");
@Test
public void severalCandidatesWithNoSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class).run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure()
.hasCauseInstanceOf(NonUniqueSessionRepositoryException.class);
assertThat(context).getFailure().hasMessageContaining(
"Multiple session repository candidates are available");
assertThat(context).getFailure().hasMessageContaining(
"set the 'spring.session.store-type' property accordingly");
});
}
@Test
public void severalCandidatesWithWrongSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class)
.withPropertyValues("spring.session.store-type=redis").run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure()
.hasCauseInstanceOf(SessionRepositoryUnavailableException.class);
assertThat(context).getFailure().hasMessageContaining(
"No session repository could be auto-configured");
assertThat(context).getFailure().hasMessageContaining(
"session store type is 'redis'");
});
}
@Test
public void severalCandidatesWithValidSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class)
.withPropertyValues("spring.session.store-type=jdbc")
.run((context) -> validateSessionRepository(context,
JdbcOperationsSessionRepository.class));
}
@Configuration
static class HazelcastConfiguration {
@Bean
@SuppressWarnings("unchecked")
public HazelcastInstance hazelcastInstance() {
IMap<Object, Object> map = mock(IMap.class);
HazelcastInstance mock = mock(HazelcastInstance.class);
given(mock.getMap("spring:session:sessions")).willReturn(map);
given(mock.getMap("foo:bar:biz")).willReturn(map);
return mock;
}
}
}

View File

@ -26,10 +26,14 @@ import org.springframework.boot.autoconfigure.DatabaseInitializationMode;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.test.context.HideClassesClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
@ -55,19 +59,35 @@ public class SessionAutoConfigurationJdbcTests
@Test
public void defaultConfig() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc")
.withConfiguration(
AutoConfigurations.of(JdbcTemplateAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=jdbc").run((context) -> {
JdbcOperationsSessionRepository repository = validateSessionRepository(
context, JdbcOperationsSessionRepository.class);
assertThat(new DirectFieldAccessor(repository)
.getPropertyValue("tableName")).isEqualTo("SPRING_SESSION");
assertThat(context.getBean(JdbcSessionProperties.class)
.getInitializeSchema())
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
assertThat(context.getBean(JdbcOperations.class)
.queryForList("select * from SPRING_SESSION")).isEmpty();
});
.run(this::validateDefaultConfig);
}
@Test
public void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new HideClassesClassLoader(
HazelcastSessionRepository.class,
RedisOperationsSessionRepository.class)
)
.withConfiguration(
AutoConfigurations.of(JdbcTemplateAutoConfiguration.class))
.run(this::validateDefaultConfig);
}
private void validateDefaultConfig(AssertableWebApplicationContext context) {
JdbcOperationsSessionRepository repository = validateSessionRepository(
context, JdbcOperationsSessionRepository.class);
assertThat(new DirectFieldAccessor(repository)
.getPropertyValue("tableName")).isEqualTo("SPRING_SESSION");
assertThat(context.getBean(JdbcSessionProperties.class)
.getInitializeSchema())
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
assertThat(context.getBean(JdbcOperations.class)
.queryForList("select * from SPRING_SESSION")).isEmpty();
}
@Test

View File

@ -22,12 +22,15 @@ import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.HideClassesClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.testsupport.rule.RedisTestServer;
import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.hazelcast.HazelcastSessionRepository;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
@ -46,10 +49,21 @@ public class SessionAutoConfigurationRedisTests
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
@Test
public void redisSessionStore() {
public void defaultConfig() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis")
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:event:created:",
RedisFlushMode.ON_SAVE));
}
@Test
public void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new HideClassesClassLoader(
HazelcastSessionRepository.class,
JdbcOperationsSessionRepository.class))
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:event:created:",
RedisFlushMode.ON_SAVE));
}

View File

@ -51,13 +51,13 @@ public class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurat
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
@Test
public void contextFailsIfStoreTypeNotSet() {
public void contextFailsIfMultipleStoresAreAvailable() {
this.contextRunner.run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure()
.hasMessageContaining("No Spring Session store is configured");
assertThat(context).getFailure()
.hasMessageContaining("set the 'spring.session.store-type' property");
.hasCauseInstanceOf(NonUniqueSessionRepositoryException.class);
assertThat(context).getFailure().hasMessageContaining(
"Multiple session repository candidates are available");
});
}
@ -67,11 +67,11 @@ public class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurat
.run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure()
.isInstanceOf(BeanCreationException.class);
.hasCauseInstanceOf(SessionRepositoryUnavailableException.class);
assertThat(context).getFailure().hasMessageContaining(
"No session repository could be auto-configured");
assertThat(context).getFailure()
.hasMessageContaining("session store type is 'jdbc'");
assertThat(context).getFailure().hasMessageContaining(
"session store type is 'jdbc'");
});
}
@ -86,16 +86,17 @@ public class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurat
public void backOffIfSessionRepositoryIsPresent() {
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
.withPropertyValues("spring.session.store-type=redis").run((context) -> {
MapSessionRepository repository = validateSessionRepository(context,
MapSessionRepository.class);
assertThat(context).getBean("mySessionRepository")
.isSameAs(repository);
});
MapSessionRepository repository = validateSessionRepository(context,
MapSessionRepository.class);
assertThat(context).getBean("mySessionRepository")
.isSameAs(repository);
});
}
@Test
public void springSessionTimeoutIsNotAValidProperty() {
this.contextRunner.withPropertyValues("spring.session.timeout=3000")
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
.withPropertyValues("spring.session.timeout=3000")
.run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure()

View File

@ -1,2 +1 @@
spring.session.store-type=redis
server.session.timeout=5

View File

@ -0,0 +1,48 @@
/*
* 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.test.context;
import java.net.URL;
import java.net.URLClassLoader;
/**
* Test {@link URLClassLoader} that hides configurable classes.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class HideClassesClassLoader extends URLClassLoader {
private final Class<?>[] hiddenClasses;
public HideClassesClassLoader(Class<?>... hiddenClasses) {
super(new URL[0], HideClassesClassLoader.class.getClassLoader());
this.hiddenClasses = hiddenClasses;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
for (Class<?> hiddenClass : this.hiddenClasses) {
if (name.equals(hiddenClass.getName())) {
throw new ClassNotFoundException();
}
}
return super.loadClass(name, resolve);
}
}