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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user