Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e6b3fda0f | ||
|
|
840da7fb5a | ||
|
|
560ee5ff4f | ||
|
|
072348e28f | ||
|
|
99dfdda7b7 | ||
|
|
18b097d9c7 | ||
|
|
702a35fac6 | ||
|
|
df3e4c5bc1 | ||
|
|
f746233255 | ||
|
|
f6c82f1eee | ||
|
|
bcdd05a0bc | ||
|
|
5d26ab4df4 | ||
|
|
e55d86f5e2 | ||
|
|
fe480b338c | ||
|
|
4b13392430 | ||
|
|
e5d9ce6ead | ||
|
|
bc1ef4359a |
@@ -4,7 +4,7 @@ buildscript {
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.2.0.M4'
|
||||
springBootVersion = '2.2.0.M5'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -1 +1 @@
|
||||
version=2.2.0.M3
|
||||
version=2.2.0.M4
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-M3'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-RC1'
|
||||
mavenBom 'org.junit:junit-bom:5.5.1'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.0.RC1'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RC2'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.M4'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.0.RC2'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RC3'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.RC1'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.12.0'
|
||||
}
|
||||
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
4
gradlew
vendored
4
gradlew
vendored
@@ -125,8 +125,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.config;
|
||||
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
|
||||
/**
|
||||
* Strategy that can be used to customize the {@link ReactiveSessionRepository} before it
|
||||
* is fully initialized, in particular to tune its configuration.
|
||||
*
|
||||
* @param <T> the {@link ReactiveSessionRepository} type
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReactiveSessionRepositoryCustomizer<T extends ReactiveSessionRepository> {
|
||||
|
||||
/**
|
||||
* Customize the {@link ReactiveSessionRepository}.
|
||||
* @param sessionRepository the {@link ReactiveSessionRepository} to customize
|
||||
*/
|
||||
void customize(T sessionRepository);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.config;
|
||||
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
/**
|
||||
* Strategy that can be used to customize the {@link SessionRepository} before it is fully
|
||||
* initialized, in particular to tune its configuration.
|
||||
*
|
||||
* @param <T> the {@link SessionRepository} type
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SessionRepositoryCustomizer<T extends SessionRepository> {
|
||||
|
||||
/**
|
||||
* Customize the {@link SessionRepository}.
|
||||
* @param sessionRepository the {@link SessionRepository} to customize
|
||||
*/
|
||||
void customize(T sessionRepository);
|
||||
|
||||
}
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -110,13 +109,8 @@ public class SpringSessionBackedSessionRegistry<S extends Session> implements Se
|
||||
* could be derived
|
||||
*/
|
||||
protected String name(Object principal) {
|
||||
if (principal instanceof UserDetails) {
|
||||
return ((UserDetails) principal).getUsername();
|
||||
}
|
||||
if (principal instanceof Principal) {
|
||||
return ((Principal) principal).getName();
|
||||
}
|
||||
return principal.toString();
|
||||
// We are reusing the logic from AbstractAuthenticationToken#getName
|
||||
return new TestingAuthenticationToken(principal, null).getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
super.addHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLengthLong(long len) {
|
||||
setContentLength(len);
|
||||
super.setContentLengthLong(len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLength(int len) {
|
||||
setContentLength((long) len);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
@@ -30,6 +31,7 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.core.AuthenticatedPrincipal;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
@@ -104,11 +106,25 @@ class SpringSessionBackedSessionRegistryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllSessions() {
|
||||
void getAllSessionsForUserDetails() {
|
||||
setUpSessions();
|
||||
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry.getAllSessions(PRINCIPAL, true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllSessionsForAuthenticatedPrincipal() {
|
||||
setUpSessions();
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry
|
||||
.getAllSessions((AuthenticatedPrincipal) () -> USER_NAME, true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllSessionsForPrincipal() {
|
||||
setUpSessions();
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry.getAllSessions(new TestPrincipal(USER_NAME),
|
||||
true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@@ -159,4 +175,40 @@ class SpringSessionBackedSessionRegistryTest {
|
||||
when(this.sessionRepository.findByPrincipalName(USER_NAME)).thenReturn(sessions);
|
||||
}
|
||||
|
||||
private static final class TestPrincipal implements Principal {
|
||||
|
||||
private final String name;
|
||||
|
||||
private TestPrincipal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object another) {
|
||||
if (this == another) {
|
||||
return true;
|
||||
}
|
||||
if (another instanceof TestPrincipal) {
|
||||
return this.name.equals(((TestPrincipal) another).name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1100,6 +1100,17 @@ class OnCommittedResponseWrapperTests {
|
||||
assertThat(this.committed).isTrue();
|
||||
}
|
||||
|
||||
// gh-7261
|
||||
@Test
|
||||
void contentLengthLongOutputStreamWriteStringCommits() throws IOException {
|
||||
String body = "something";
|
||||
this.response.setContentLengthLong(body.length());
|
||||
|
||||
this.response.getOutputStream().print(body);
|
||||
|
||||
assertThat(this.committed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferSizeCommitsOnce() throws Exception {
|
||||
String expected = "1234567890";
|
||||
|
||||
@@ -18,8 +18,10 @@ package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -51,6 +53,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
@@ -103,6 +106,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private Executor redisSubscriptionExecutor;
|
||||
|
||||
private List<SessionRepositoryCustomizer<RedisOperationsSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
@@ -123,6 +128,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -221,6 +228,12 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<RedisOperationsSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -36,6 +38,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
@@ -68,6 +71,8 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private List<ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
@@ -82,6 +87,8 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -120,6 +127,12 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
@@ -57,6 +59,8 @@ import static org.mockito.Mockito.mock;
|
||||
@SuppressWarnings("deprecation")
|
||||
class RedisHttpSessionConfigurationTests {
|
||||
|
||||
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
|
||||
|
||||
private static final String CLEANUP_CRON_EXPRESSION = "0 0 * * * *";
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
@@ -238,6 +242,15 @@ class RedisHttpSessionConfigurationTests {
|
||||
assertThat(beans).containsKeys("springSessionRedisMessageListenerContainer", "redisMessageListenerContainer");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionRepositoryCustomizer() {
|
||||
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
|
||||
RedisOperationsSessionRepository sessionRepository = this.context
|
||||
.getBean(RedisOperationsSessionRepository.class);
|
||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -416,4 +429,22 @@ class RedisHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisHttpSession
|
||||
static class SessionRepositoryCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public SessionRepositoryCustomizer<RedisOperationsSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public SessionRepositoryCustomizer<RedisOperationsSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository
|
||||
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,11 +25,13 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
@@ -222,6 +224,15 @@ class RedisWebSessionConfigurationTests {
|
||||
"serializer")).isEqualTo(redisSerializer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionRepositoryCustomizer() {
|
||||
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
|
||||
ReactiveRedisOperationsSessionRepository sessionRepository = this.context
|
||||
.getBean(ReactiveRedisOperationsSessionRepository.class);
|
||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -348,4 +359,22 @@ class RedisWebSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession
|
||||
static class SessionRepositoryCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public ReactiveSessionRepositoryCustomizer<ReactiveRedisOperationsSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository
|
||||
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
@@ -42,7 +41,7 @@ import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -162,14 +161,14 @@ class IndexDocTests {
|
||||
// tag::new-jdbcoperationssessionrepository[]
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate();
|
||||
|
||||
// ... configure JdbcTemplate ...
|
||||
// ... configure jdbcTemplate ...
|
||||
|
||||
PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate();
|
||||
|
||||
// ... configure transactionManager ...
|
||||
// ... configure transactionTemplate ...
|
||||
|
||||
SessionRepository<? extends Session> repository = new JdbcOperationsSessionRepository(jdbcTemplate,
|
||||
transactionManager);
|
||||
transactionTemplate);
|
||||
// end::new-jdbcoperationssessionrepository[]
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.session.hazelcast.config.annotation.web.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
@@ -31,6 +33,7 @@ import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.hazelcast.HazelcastFlushMode;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
@@ -63,6 +66,8 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private List<SessionRepositoryCustomizer<HazelcastSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
@Bean
|
||||
public HazelcastSessionRepository sessionRepository() {
|
||||
HazelcastSessionRepository sessionRepository = new HazelcastSessionRepository(this.hazelcastInstance);
|
||||
@@ -73,6 +78,8 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
sessionRepository.setFlushMode(this.flushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -113,6 +120,12 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<HazelcastSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
|
||||
@@ -26,8 +26,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.hazelcast.HazelcastFlushMode;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.session.hazelcast.config.annotation.SpringSessionHazelcastInstance;
|
||||
@@ -222,6 +224,14 @@ class HazelcastHttpSessionConfigurationTests {
|
||||
.withMessageContaining("expected single matching bean but found 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionRepositoryCustomizer() {
|
||||
registerAndRefresh(SessionRepositoryCustomizerConfiguration.class);
|
||||
HazelcastSessionRepository sessionRepository = this.context.getBean(HazelcastSessionRepository.class);
|
||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -421,4 +431,22 @@ class HazelcastHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableHazelcastHttpSession
|
||||
static class SessionRepositoryCustomizerConfiguration extends BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public SessionRepositoryCustomizer<HazelcastSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public SessionRepositoryCustomizer<HazelcastSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository
|
||||
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ final class DatabaseContainers {
|
||||
private static class PostgreSql9Container extends PostgreSQLContainer<PostgreSql9Container> {
|
||||
|
||||
PostgreSql9Container() {
|
||||
super("postgres:9.6.14");
|
||||
super("postgres:9.6.15");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,7 +160,7 @@ final class DatabaseContainers {
|
||||
private static class PostgreSql10Container extends PostgreSQLContainer<PostgreSql10Container> {
|
||||
|
||||
PostgreSql10Container() {
|
||||
super("postgres:10.9");
|
||||
super("postgres:10.10");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -168,7 +168,7 @@ final class DatabaseContainers {
|
||||
private static class PostgreSql11Container extends PostgreSQLContainer<PostgreSql11Container> {
|
||||
|
||||
PostgreSql11Container() {
|
||||
super("postgres:11.4");
|
||||
super("postgres:11.5");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
@@ -56,8 +55,6 @@ import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -75,16 +72,16 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* // ... configure jdbcTemplate ...
|
||||
*
|
||||
* PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
|
||||
* TransactionTemplate transactionTemplate = new TransactionTemplate();
|
||||
*
|
||||
* // ... configure transactionManager ...
|
||||
* // ... configure transactionTemplate ...
|
||||
*
|
||||
* JdbcOperationsSessionRepository sessionRepository =
|
||||
* new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
|
||||
* new JdbcOperationsSessionRepository(jdbcTemplate, transactionTemplate);
|
||||
* </pre>
|
||||
*
|
||||
* For additional information on how to create and configure {@link JdbcTemplate} and
|
||||
* {@link PlatformTransactionManager}, refer to the <a href=
|
||||
* For additional information on how to create and configure {@code JdbcTemplate} and
|
||||
* {@code TransactionTemplate}, refer to the <a href=
|
||||
* "https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html">
|
||||
* Spring Framework Reference Documentation</a>.
|
||||
* <p>
|
||||
@@ -200,12 +197,12 @@ public class JdbcOperationsSessionRepository
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private final TransactionOperations transactionOperations;
|
||||
|
||||
private final ResultSetExtractor<List<JdbcSession>> extractor = new SessionResultSetExtractor();
|
||||
|
||||
private final IndexResolver<JdbcSession> indexResolver;
|
||||
|
||||
private TransactionOperations transactionOperations = TransactionOperations.withoutTransaction();
|
||||
|
||||
/**
|
||||
* The name of database table used by Spring Session to store sessions.
|
||||
*/
|
||||
@@ -237,12 +234,30 @@ public class JdbcOperationsSessionRepository
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private LobHandler lobHandler = new DefaultLobHandler();
|
||||
private LobHandler lobHandler;
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcOperationsSessionRepository} instance which uses the
|
||||
* provided {@link JdbcOperations} and {@link TransactionOperations} to manage
|
||||
* sessions.
|
||||
* @param jdbcOperations the {@link JdbcOperations} to use
|
||||
* @param transactionOperations the {@link TransactionOperations} to use
|
||||
*/
|
||||
public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations, TransactionOperations transactionOperations) {
|
||||
Assert.notNull(jdbcOperations, "jdbcOperations must not be null");
|
||||
Assert.notNull(transactionOperations, "transactionOperations must not be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.transactionOperations = transactionOperations;
|
||||
this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
|
||||
this.conversionService = createDefaultConversionService();
|
||||
this.lobHandler = new DefaultLobHandler();
|
||||
prepareQueries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcOperationsSessionRepository} instance which uses the
|
||||
* provided {@link JdbcOperations} to manage sessions.
|
||||
@@ -251,12 +266,13 @@ public class JdbcOperationsSessionRepository
|
||||
* propagation level of {@link TransactionDefinition#PROPAGATION_REQUIRES_NEW}.
|
||||
* @param jdbcOperations the {@link JdbcOperations} to use
|
||||
* @param transactionManager the {@link PlatformTransactionManager} to use
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link #JdbcOperationsSessionRepository(JdbcOperations, TransactionOperations)}
|
||||
*/
|
||||
@Deprecated
|
||||
public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations,
|
||||
PlatformTransactionManager transactionManager) {
|
||||
this(jdbcOperations);
|
||||
Assert.notNull(transactionManager, "TransactionManager must not be null");
|
||||
this.transactionOperations = createTransactionTemplate(transactionManager);
|
||||
this(jdbcOperations, createTransactionTemplate(transactionManager));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,13 +281,12 @@ public class JdbcOperationsSessionRepository
|
||||
* <p>
|
||||
* The created instance will not execute data access operations in a transaction.
|
||||
* @param jdbcOperations the {@link JdbcOperations} to use
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link #JdbcOperationsSessionRepository(JdbcOperations, TransactionOperations)}
|
||||
*/
|
||||
@Deprecated
|
||||
public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations) {
|
||||
Assert.notNull(jdbcOperations, "JdbcOperations must not be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
|
||||
this.conversionService = createDefaultConversionService();
|
||||
prepareQueries();
|
||||
this(jdbcOperations, TransactionOperations.withoutTransaction());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,15 +463,8 @@ public class JdbcOperationsSessionRepository
|
||||
|
||||
@Override
|
||||
public void deleteById(final String id) {
|
||||
this.transactionOperations.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.deleteSessionQuery, id);
|
||||
}
|
||||
|
||||
});
|
||||
this.transactionOperations.execute(() -> JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.deleteSessionQuery, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -581,6 +589,7 @@ public class JdbcOperationsSessionRepository
|
||||
}
|
||||
|
||||
private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
|
||||
Assert.notNull(transactionManager, "transactionManager must not be null");
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
transactionTemplate.afterPropertiesSet();
|
||||
@@ -805,71 +814,59 @@ public class JdbcOperationsSessionRepository
|
||||
|
||||
private void save() {
|
||||
if (this.isNew) {
|
||||
JdbcOperationsSessionRepository.this.transactionOperations
|
||||
.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
Map<String, String> indexes = JdbcOperationsSessionRepository.this.indexResolver
|
||||
.resolveIndexesFor(JdbcSession.this);
|
||||
JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.createSessionQuery, (ps) -> {
|
||||
ps.setString(1, JdbcSession.this.primaryKey);
|
||||
ps.setString(2, getId());
|
||||
ps.setLong(3, getCreationTime().toEpochMilli());
|
||||
ps.setLong(4, getLastAccessedTime().toEpochMilli());
|
||||
ps.setInt(5, (int) getMaxInactiveInterval().getSeconds());
|
||||
ps.setLong(6, getExpiryTime().toEpochMilli());
|
||||
ps.setString(7, indexes.get(PRINCIPAL_NAME_INDEX_NAME));
|
||||
});
|
||||
Set<String> attributeNames = getAttributeNames();
|
||||
if (!attributeNames.isEmpty()) {
|
||||
insertSessionAttributes(JdbcSession.this, new ArrayList<>(attributeNames));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
JdbcOperationsSessionRepository.this.transactionOperations.execute(() -> {
|
||||
Map<String, String> indexes = JdbcOperationsSessionRepository.this.indexResolver
|
||||
.resolveIndexesFor(JdbcSession.this);
|
||||
JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.createSessionQuery, (ps) -> {
|
||||
ps.setString(1, JdbcSession.this.primaryKey);
|
||||
ps.setString(2, getId());
|
||||
ps.setLong(3, getCreationTime().toEpochMilli());
|
||||
ps.setLong(4, getLastAccessedTime().toEpochMilli());
|
||||
ps.setInt(5, (int) getMaxInactiveInterval().getSeconds());
|
||||
ps.setLong(6, getExpiryTime().toEpochMilli());
|
||||
ps.setString(7, indexes.get(PRINCIPAL_NAME_INDEX_NAME));
|
||||
});
|
||||
Set<String> attributeNames = getAttributeNames();
|
||||
if (!attributeNames.isEmpty()) {
|
||||
insertSessionAttributes(JdbcSession.this, new ArrayList<>(attributeNames));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
JdbcOperationsSessionRepository.this.transactionOperations
|
||||
.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
if (JdbcSession.this.changed) {
|
||||
Map<String, String> indexes = JdbcOperationsSessionRepository.this.indexResolver
|
||||
.resolveIndexesFor(JdbcSession.this);
|
||||
JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.updateSessionQuery, (ps) -> {
|
||||
ps.setString(1, getId());
|
||||
ps.setLong(2, getLastAccessedTime().toEpochMilli());
|
||||
ps.setInt(3, (int) getMaxInactiveInterval().getSeconds());
|
||||
ps.setLong(4, getExpiryTime().toEpochMilli());
|
||||
ps.setString(5, indexes.get(PRINCIPAL_NAME_INDEX_NAME));
|
||||
ps.setString(6, JdbcSession.this.primaryKey);
|
||||
});
|
||||
}
|
||||
List<String> addedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.ADDED).map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
if (!addedAttributeNames.isEmpty()) {
|
||||
insertSessionAttributes(JdbcSession.this, addedAttributeNames);
|
||||
}
|
||||
List<String> updatedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.UPDATED)
|
||||
.map(Map.Entry::getKey).collect(Collectors.toList());
|
||||
if (!updatedAttributeNames.isEmpty()) {
|
||||
updateSessionAttributes(JdbcSession.this, updatedAttributeNames);
|
||||
}
|
||||
List<String> removedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.REMOVED)
|
||||
.map(Map.Entry::getKey).collect(Collectors.toList());
|
||||
if (!removedAttributeNames.isEmpty()) {
|
||||
deleteSessionAttributes(JdbcSession.this, removedAttributeNames);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
JdbcOperationsSessionRepository.this.transactionOperations.execute(() -> {
|
||||
if (JdbcSession.this.changed) {
|
||||
Map<String, String> indexes = JdbcOperationsSessionRepository.this.indexResolver
|
||||
.resolveIndexesFor(JdbcSession.this);
|
||||
JdbcOperationsSessionRepository.this.jdbcOperations
|
||||
.update(JdbcOperationsSessionRepository.this.updateSessionQuery, (ps) -> {
|
||||
ps.setString(1, getId());
|
||||
ps.setLong(2, getLastAccessedTime().toEpochMilli());
|
||||
ps.setInt(3, (int) getMaxInactiveInterval().getSeconds());
|
||||
ps.setLong(4, getExpiryTime().toEpochMilli());
|
||||
ps.setString(5, indexes.get(PRINCIPAL_NAME_INDEX_NAME));
|
||||
ps.setString(6, JdbcSession.this.primaryKey);
|
||||
});
|
||||
}
|
||||
List<String> addedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.ADDED).map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
if (!addedAttributeNames.isEmpty()) {
|
||||
insertSessionAttributes(JdbcSession.this, addedAttributeNames);
|
||||
}
|
||||
List<String> updatedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.UPDATED).map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
if (!updatedAttributeNames.isEmpty()) {
|
||||
updateSessionAttributes(JdbcSession.this, updatedAttributeNames);
|
||||
}
|
||||
List<String> removedAttributeNames = JdbcSession.this.delta.entrySet().stream()
|
||||
.filter((entry) -> entry.getValue() == DeltaValue.REMOVED).map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
if (!removedAttributeNames.isEmpty()) {
|
||||
deleteSessionAttributes(JdbcSession.this, removedAttributeNames);
|
||||
}
|
||||
});
|
||||
}
|
||||
clearChangeFlags();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.session.jdbc.config.annotation.web.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -45,11 +47,15 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
@@ -87,12 +93,16 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private TransactionOperations transactionOperations;
|
||||
|
||||
private LobHandler lobHandler;
|
||||
|
||||
private ConversionService springSessionConversionService;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private List<SessionRepositoryCustomizer<JdbcOperationsSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
@@ -100,8 +110,11 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
@Bean
|
||||
public JdbcOperationsSessionRepository sessionRepository() {
|
||||
JdbcTemplate jdbcTemplate = createJdbcTemplate(this.dataSource);
|
||||
if (this.transactionOperations == null) {
|
||||
this.transactionOperations = createTransactionTemplate(this.transactionManager);
|
||||
}
|
||||
JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(jdbcTemplate,
|
||||
this.transactionManager);
|
||||
this.transactionOperations);
|
||||
if (StringUtils.hasText(this.tableName)) {
|
||||
sessionRepository.setTableName(this.tableName);
|
||||
}
|
||||
@@ -123,8 +136,10 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
sessionRepository.setConversionService(this.conversionService);
|
||||
}
|
||||
else {
|
||||
sessionRepository.setConversionService(createConversionServiceWithBeanClassLoader());
|
||||
sessionRepository.setConversionService(createConversionServiceWithBeanClassLoader(this.classLoader));
|
||||
}
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -173,6 +188,12 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionTransactionOperations")
|
||||
public void setTransactionOperations(TransactionOperations transactionOperations) {
|
||||
this.transactionOperations = transactionOperations;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionLobHandler")
|
||||
public void setLobHandler(LobHandler lobHandler) {
|
||||
@@ -191,6 +212,12 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<JdbcOperationsSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
@@ -230,10 +257,17 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
return jdbcTemplate;
|
||||
}
|
||||
|
||||
private GenericConversionService createConversionServiceWithBeanClassLoader() {
|
||||
private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) {
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
transactionTemplate.afterPropertiesSet();
|
||||
return transactionTemplate;
|
||||
}
|
||||
|
||||
private static GenericConversionService createConversionServiceWithBeanClassLoader(ClassLoader classLoader) {
|
||||
GenericConversionService conversionService = new GenericConversionService();
|
||||
conversionService.addConverter(Object.class, byte[].class, new SerializingConverter());
|
||||
conversionService.addConverter(byte[].class, Object.class, new DeserializingConverter(this.classLoader));
|
||||
conversionService.addConverter(byte[].class, Object.class, new DeserializingConverter(classLoader));
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import java.util.function.Supplier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@@ -43,24 +45,18 @@ import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository.JdbcSession;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.endsWith;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link JdbcOperationsSessionRepository}.
|
||||
@@ -73,29 +69,39 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private JdbcOperations jdbcOperations = mock(JdbcOperations.class);
|
||||
|
||||
private PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class);
|
||||
@Mock
|
||||
private JdbcOperations jdbcOperations;
|
||||
|
||||
private JdbcOperationsSessionRepository repository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations, this.transactionManager);
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations,
|
||||
TransactionOperations.withoutTransaction());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorNullJdbcOperations() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcOperationsSessionRepository(null, this.transactionManager))
|
||||
.withMessage("JdbcOperations must not be null");
|
||||
.isThrownBy(() -> new JdbcOperationsSessionRepository(null, TransactionOperations.withoutTransaction()))
|
||||
.withMessage("jdbcOperations must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorNullTransactionManager() {
|
||||
void constructorNullTransactionOperations() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcOperationsSessionRepository(this.jdbcOperations, null))
|
||||
.withMessage("TransactionManager must not be null");
|
||||
.isThrownBy(
|
||||
() -> new JdbcOperationsSessionRepository(this.jdbcOperations, (TransactionOperations) null))
|
||||
.withMessage("transactionOperations must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void constructorNullTransactionManager() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new JdbcOperationsSessionRepository(this.jdbcOperations, (PlatformTransactionManager) null))
|
||||
.withMessage("transactionManager must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -248,7 +254,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
|
||||
assertThat(session.isNew()).isTrue();
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -260,7 +266,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
|
||||
assertThat(session.isNew()).isTrue();
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -268,7 +274,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.setFlushMode(FlushMode.IMMEDIATE);
|
||||
JdbcSession session = this.repository.createSession();
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("INSERT"), isA(PreparedStatementSetter.class));
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
@@ -280,9 +285,8 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT"), isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -293,12 +297,11 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -310,12 +313,11 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(BatchPreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -327,10 +329,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -343,10 +344,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(BatchPreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -360,10 +360,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -379,10 +378,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"),
|
||||
isA(BatchPreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -396,10 +394,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -411,8 +408,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -428,10 +424,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"),
|
||||
isA(BatchPreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test // gh-1070
|
||||
@@ -444,10 +439,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test // gh-1070
|
||||
@@ -460,8 +454,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test // gh-1070
|
||||
@@ -476,10 +469,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test // gh-1070
|
||||
@@ -494,10 +486,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -509,10 +500,9 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION SET"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -523,7 +513,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
|
||||
assertThat(session.isNew()).isFalse();
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -536,7 +526,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(sessionId);
|
||||
|
||||
assertThat(session).isNull();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
|
||||
isA(ResultSetExtractor.class));
|
||||
}
|
||||
@@ -552,7 +541,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(expired.getId());
|
||||
|
||||
assertThat(session).isNull();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
|
||||
isA(ResultSetExtractor.class));
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(expired.getId()));
|
||||
@@ -571,7 +559,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
assertThat(session.getId()).isEqualTo(saved.getId());
|
||||
assertThat(session.isNew()).isFalse();
|
||||
assertThat(session.<String>getAttribute("savedName")).isEqualTo("savedValue");
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
|
||||
isA(ResultSetExtractor.class));
|
||||
}
|
||||
@@ -582,7 +569,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
|
||||
this.repository.deleteById(sessionId);
|
||||
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(sessionId));
|
||||
}
|
||||
|
||||
@@ -594,7 +580,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
.findByIndexNameAndIndexValue("testIndexName", indexValue);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -608,7 +594,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).isEmpty();
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
|
||||
isA(ResultSetExtractor.class));
|
||||
}
|
||||
@@ -633,7 +618,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
|
||||
|
||||
assertThat(sessions).hasSize(2);
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
|
||||
isA(ResultSetExtractor.class));
|
||||
}
|
||||
@@ -642,7 +626,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
void cleanupExpiredSessions() {
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), anyLong());
|
||||
}
|
||||
|
||||
@@ -659,84 +642,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveNewWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveUpdatedWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(new MapSession(),
|
||||
"primaryKey", false);
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void findByIdWithoutTransaction() {
|
||||
given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class), any(ResultSetExtractor.class)))
|
||||
.willReturn(Collections.emptyList());
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.findById("testSessionId");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).query(endsWith("WHERE S.SESSION_ID = ?"),
|
||||
isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteByIdWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.deleteById("testSessionId");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(eq("DELETE FROM SPRING_SESSION WHERE SESSION_ID = ?"),
|
||||
anyString());
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void findByIndexNameAndIndexValueWithoutTransaction() {
|
||||
given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class), any(ResultSetExtractor.class)))
|
||||
.willReturn(Collections.emptyList());
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
"testIndexValue");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).query(endsWith("WHERE S.PRINCIPAL_NAME = ?"),
|
||||
isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanUpExpiredSessionsWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(eq("DELETE FROM SPRING_SESSION WHERE EXPIRY_TIME < ?"), anyLong());
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnSetAttribute() {
|
||||
this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
@@ -751,7 +656,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.save(session);
|
||||
verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -770,7 +675,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
.forClass(BatchPreparedStatementSetter.class);
|
||||
verify(this.jdbcOperations).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), captor.capture());
|
||||
assertThat(captor.getValue().getBatchSize()).isEqualTo(2);
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -789,7 +694,7 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
.forClass(BatchPreparedStatementSetter.class);
|
||||
verify(this.jdbcOperations).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), captor.capture());
|
||||
assertThat(captor.getValue().getBatchSize()).isEqualTo(3);
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -798,7 +703,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false);
|
||||
String attrName = "someAttribute";
|
||||
session.setAttribute(attrName, "someValue");
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
@@ -811,7 +715,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
cached.setAttribute("attribute1", "value1");
|
||||
JdbcSession session = this.repository.new JdbcSession(cached, "primaryKey", false);
|
||||
session.removeAttribute("attribute1");
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
@@ -822,7 +725,6 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.setFlushMode(FlushMode.IMMEDIATE);
|
||||
JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false);
|
||||
session.setMaxInactiveInterval(Duration.ofSeconds(1));
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class));
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
@@ -832,16 +734,8 @@ class JdbcOperationsSessionRepositoryTests {
|
||||
this.repository.setFlushMode(FlushMode.IMMEDIATE);
|
||||
JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false);
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
assertPropagationRequiresNew();
|
||||
verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class));
|
||||
verifyNoMoreInteractions(this.jdbcOperations);
|
||||
}
|
||||
|
||||
private void assertPropagationRequiresNew() {
|
||||
ArgumentCaptor<TransactionDefinition> argument = ArgumentCaptor.forClass(TransactionDefinition.class);
|
||||
verify(this.transactionManager, atLeastOnce()).getTransaction(argument.capture());
|
||||
assertThat(argument.getValue().getPropagationBehavior())
|
||||
.isEqualTo(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,16 +27,20 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -77,7 +81,10 @@ class JdbcHttpSessionConfigurationTests {
|
||||
void defaultConfiguration() {
|
||||
registerAndRefresh(DataSourceConfiguration.class, DefaultConfiguration.class);
|
||||
|
||||
assertThat(this.context.getBean(JdbcOperationsSessionRepository.class)).isNotNull();
|
||||
JdbcOperationsSessionRepository sessionRepository = this.context.getBean(JdbcOperationsSessionRepository.class);
|
||||
assertThat(sessionRepository).isNotNull();
|
||||
assertThat(sessionRepository).extracting("transactionOperations")
|
||||
.hasFieldOrPropertyWithValue("propagationBehavior", TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -225,6 +232,17 @@ class JdbcHttpSessionConfigurationTests {
|
||||
.withMessageContaining("expected single matching bean but found 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customTransactionOperationsConfiguration() {
|
||||
registerAndRefresh(DataSourceConfiguration.class, CustomTransactionOperationsConfiguration.class);
|
||||
|
||||
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
|
||||
TransactionOperations transactionOperations = this.context.getBean(TransactionOperations.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(transactionOperations).isNotNull();
|
||||
assertThat(repository).hasFieldOrPropertyWithValue("transactionOperations", transactionOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customLobHandlerConfiguration() {
|
||||
registerAndRefresh(DataSourceConfiguration.class, CustomLobHandlerConfiguration.class);
|
||||
@@ -258,6 +276,14 @@ class JdbcHttpSessionConfigurationTests {
|
||||
assertThat(ReflectionTestUtils.getField(configuration, "tableName")).isEqualTo("custom_session_table");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionRepositoryCustomizer() {
|
||||
registerAndRefresh(DataSourceConfiguration.class, SessionRepositoryCustomizerConfiguration.class);
|
||||
JdbcOperationsSessionRepository sessionRepository = this.context.getBean(JdbcOperationsSessionRepository.class);
|
||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
|
||||
MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -417,6 +443,16 @@ class JdbcHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableJdbcHttpSession
|
||||
static class CustomTransactionOperationsConfiguration {
|
||||
|
||||
@Bean
|
||||
public TransactionOperations springSessionTransactionOperations() {
|
||||
return TransactionOperations.withoutTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableJdbcHttpSession
|
||||
static class CustomLobHandlerConfiguration {
|
||||
|
||||
@@ -447,4 +483,22 @@ class JdbcHttpSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableJdbcHttpSession
|
||||
static class SessionRepositoryCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public SessionRepositoryCustomizer<JdbcOperationsSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public SessionRepositoryCustomizer<JdbcOperationsSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository
|
||||
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
ext['spring-framework.version'] = '5.2.0.RC1'
|
||||
|
||||
dependencyManagement {
|
||||
dependencies {
|
||||
dependency 'ch.qos.logback:logback-classic:1.2.3'
|
||||
|
||||
Reference in New Issue
Block a user