Compare commits
50 Commits
2.1.0.M3
...
2.1.1.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05986d68b2 | ||
|
|
e17b047800 | ||
|
|
5ab2424b14 | ||
|
|
196919efbb | ||
|
|
717e16cb71 | ||
|
|
5f1b7d6722 | ||
|
|
4d3a01919c | ||
|
|
e408d7f557 | ||
|
|
f34acebf84 | ||
|
|
1aab3e8285 | ||
|
|
c3528996d2 | ||
|
|
3ccc3eb6e1 | ||
|
|
de76be95ac | ||
|
|
bc127ab3fc | ||
|
|
3e9f6a35c4 | ||
|
|
49daa3a9c7 | ||
|
|
a67bd634d9 | ||
|
|
2762f001bf | ||
|
|
93aee206fb | ||
|
|
3df3b30117 | ||
|
|
5fb0c4dd35 | ||
|
|
6fbce6e3e8 | ||
|
|
a3fd05326a | ||
|
|
4c6dc976b3 | ||
|
|
58ae28b0a0 | ||
|
|
3e98ecf234 | ||
|
|
41ed429f98 | ||
|
|
def15b05ca | ||
|
|
eae8592f2b | ||
|
|
81460ede09 | ||
|
|
ca4ec9a557 | ||
|
|
fd2165f471 | ||
|
|
ad1e57a1fe | ||
|
|
0ffcaa2d35 | ||
|
|
b61937def7 | ||
|
|
c523fb591d | ||
|
|
227fab2e42 | ||
|
|
7f7815d80c | ||
|
|
002136bad4 | ||
|
|
1085661984 | ||
|
|
12bb0741bb | ||
|
|
eecdcb49d9 | ||
|
|
3e1a22102d | ||
|
|
9f6e791e5d | ||
|
|
efc35eddad | ||
|
|
4c37ec9f4a | ||
|
|
1a3da5944d | ||
|
|
5d0775b802 | ||
|
|
603a258172 | ||
|
|
22ebe65931 |
40
Jenkinsfile
vendored
40
Jenkinsfile
vendored
@@ -11,8 +11,8 @@ currentBuild.result = SUCCESS
|
||||
try {
|
||||
parallel check: {
|
||||
stage('Check') {
|
||||
timeout(time: 30, unit: 'MINUTES') {
|
||||
node {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
sh './gradlew clean check --no-daemon --refresh-dependencies'
|
||||
@@ -27,6 +27,42 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk10'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk11: {
|
||||
stage('JDK 11') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk11'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk11'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentBuild.result == 'SUCCESS') {
|
||||
|
||||
38
build.gradle
38
build.gradle
@@ -1,20 +1,38 @@
|
||||
buildscript {
|
||||
ext {
|
||||
releaseBuild = version.endsWith('RELEASE')
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.1.0.M3'
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.18.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.19.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/plugins-release' }
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
|
||||
group = 'org.springframework.session'
|
||||
description = 'Spring Session'
|
||||
|
||||
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
def jacocoEnabled = graph.allTasks.any { it instanceof JacocoReport }
|
||||
subprojects {
|
||||
plugins.withType(JavaPlugin) {
|
||||
sourceCompatibility = 1.8
|
||||
}
|
||||
plugins.withType(JacocoPlugin) {
|
||||
tasks.withType(Test) {
|
||||
jacoco.enabled = jacocoEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
|
||||
@@ -54,7 +53,7 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||
|
||||
// tag::rememberme-bean[]
|
||||
@Bean
|
||||
RememberMeServices rememberMeServices() {
|
||||
public SpringSessionRememberMeServices rememberMeServices() {
|
||||
SpringSessionRememberMeServices rememberMeServices =
|
||||
new SpringSessionRememberMeServices();
|
||||
// optionally customize
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
springBootVersion=2.1.0.M2
|
||||
version=2.1.0.M3
|
||||
version=2.1.1.RELEASE
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Californium-RC1'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.1.0.RC3'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Lovelace-RC2'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.1.0.RC2'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.8.3'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Californium-SR2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.1.2.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Lovelace-SR2'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.1.1.RELEASE'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.9.1'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.10.4') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.10.6') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
|
||||
dependency 'com.h2database:h2:1.4.197'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.0.0.jre8'
|
||||
dependency 'com.zaxxer:HikariCP:3.2.0'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.0.RC1'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.1.RELEASE'
|
||||
dependency 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
dependency 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
dependency 'junit:junit:4.12'
|
||||
@@ -26,7 +27,7 @@ dependencyManagement {
|
||||
dependency 'org.assertj:assertj-core:3.11.1'
|
||||
dependency 'org.hsqldb:hsqldb:2.4.1'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.3.0'
|
||||
dependency 'org.mockito:mockito-core:2.22.0'
|
||||
dependency 'org.mockito:mockito-core:2.23.0'
|
||||
dependency 'org.postgresql:postgresql:42.2.5'
|
||||
}
|
||||
}
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
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-4.10-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -23,5 +23,6 @@ dependencies {
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "org.testcontainers:testcontainers"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -42,7 +42,8 @@ public class SessionConfig {
|
||||
int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
config.getNetworkConfig()
|
||||
.setPort(port);
|
||||
.setPort(port)
|
||||
.getJoin().getMulticastConfig().setEnabled(false);
|
||||
|
||||
System.out.println("Hazelcast port #: " + port);
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -17,8 +17,6 @@ dependencies {
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.springframework:spring-test"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
gretty {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -64,7 +64,9 @@ public class Initializer implements ServletContextListener {
|
||||
private HazelcastInstance createHazelcastInstance() {
|
||||
Config config = new Config();
|
||||
|
||||
config.getNetworkConfig().setPort(getAvailablePort());
|
||||
config.getNetworkConfig()
|
||||
.setPort(getAvailablePort())
|
||||
.getJoin().getMulticastConfig().setEnabled(false);
|
||||
|
||||
config.getMapConfig(SESSION_MAP_NAME)
|
||||
.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -2,12 +2,15 @@ rootProject.name = 'spring-session-build'
|
||||
|
||||
FileTree buildFiles = fileTree(rootDir) {
|
||||
include '**/*.gradle'
|
||||
exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'
|
||||
exclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
|
||||
exclude '**/grails3'
|
||||
gradle.startParameter.projectProperties.get('excludeProjects')?.split(',')?.each { excludeProject ->
|
||||
exclude excludeProject
|
||||
}
|
||||
}
|
||||
|
||||
String rootDirPath = rootDir.absolutePath + File.separator
|
||||
buildFiles.each { File buildFile ->
|
||||
buildFiles.each { buildFile ->
|
||||
if (buildFile.name == 'build.gradle') {
|
||||
String buildFilePath = buildFile.parentFile.absolutePath
|
||||
String projectPath = buildFilePath.replace(rootDirPath, '').replace(File.separator, ':')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface HttpSessionIdResolver {
|
||||
|
||||
|
||||
@@ -205,6 +205,8 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private boolean requestedSessionCached;
|
||||
|
||||
private String requestedSessionId;
|
||||
|
||||
private Boolean requestedSessionIdValid;
|
||||
|
||||
private boolean requestedSessionInvalidated;
|
||||
@@ -277,7 +279,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
return isRequestedSessionIdValid(requestedSession);
|
||||
}
|
||||
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
@@ -351,8 +352,10 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
@Override
|
||||
public String getRequestedSessionId() {
|
||||
S requestedSession = getRequestedSession();
|
||||
return (requestedSession != null) ? requestedSession.getId() : null;
|
||||
if (this.requestedSessionId == null) {
|
||||
getRequestedSession();
|
||||
}
|
||||
return this.requestedSessionId;
|
||||
}
|
||||
|
||||
private S getRequestedSession() {
|
||||
@@ -360,10 +363,14 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
|
||||
.resolveSessionIds(this);
|
||||
for (String sessionId : sessionIds) {
|
||||
if (this.requestedSessionId == null) {
|
||||
this.requestedSessionId = sessionId;
|
||||
}
|
||||
S session = SessionRepositoryFilter.this.sessionRepository
|
||||
.findById(sessionId);
|
||||
if (session != null) {
|
||||
this.requestedSession = session;
|
||||
this.requestedSessionId = sessionId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -375,6 +382,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
private void clearRequestedSessionCache() {
|
||||
this.requestedSessionCached = false;
|
||||
this.requestedSession = null;
|
||||
this.requestedSessionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -74,7 +75,9 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
}
|
||||
@@ -90,7 +93,9 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
}
|
||||
|
||||
@@ -1196,6 +1196,29 @@ public class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // gh-1229
|
||||
public void doFilterAdapterGetRequestedSessionIdForInvalidSession() throws Exception {
|
||||
SessionRepository<MapSession> sessionRepository = new MapSessionRepository(
|
||||
new HashMap<>());
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
this.filter.setHttpSessionIdResolver(this.strategy);
|
||||
final String expectedId = "HttpSessionIdResolver-requested-id1";
|
||||
final String otherId = "HttpSessionIdResolver-requested-id2";
|
||||
|
||||
given(this.strategy.resolveSessionIds(any(HttpServletRequest.class)))
|
||||
.willReturn(Arrays.asList(expectedId, otherId));
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest,
|
||||
HttpServletResponse wrappedResponse) {
|
||||
assertThat(wrappedRequest.getRequestedSessionId()).isEqualTo(expectedId);
|
||||
assertThat(wrappedRequest.isRequestedSessionIdValid()).isFalse();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterOnNewSession() throws Exception {
|
||||
this.filter.setHttpSessionIdResolver(this.strategy);
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ReactiveRedisOperationsSessionRepository}.
|
||||
@@ -209,7 +210,10 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
this.repository.save(session).block();
|
||||
|
||||
toSave.setLastAccessedTime(Instant.now());
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> this.repository.save(toSave).block())
|
||||
.withMessage("Session was invalidated");
|
||||
|
||||
assertThat(this.repository.findById(sessionId).block()).isNull();
|
||||
assertThat(this.repository.findById(session.getId()).block()).isNotNull();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -190,9 +191,10 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
body.getBytes("UTF-8"));
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
@@ -358,9 +360,10 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
body.getBytes("UTF-8"));
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -156,7 +156,9 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
session.hasChangedSessionId() ? session.originalSessionId
|
||||
: session.getId());
|
||||
return this.sessionRedisOperations.hasKey(sessionKey)
|
||||
.flatMap((exists) -> exists ? result : Mono.empty());
|
||||
.flatMap((exists) -> exists ? result
|
||||
: Mono.error(new IllegalStateException(
|
||||
"Session was invalidated")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,11 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
static PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver();
|
||||
|
||||
/**
|
||||
* The default Redis database used by Spring Session.
|
||||
*/
|
||||
public static final int DEFAULT_DATABASE = 0;
|
||||
|
||||
/**
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
*/
|
||||
@@ -286,11 +291,19 @@ public class RedisOperationsSessionRepository implements
|
||||
*/
|
||||
static final String SESSION_ATTR_PREFIX = "sessionAttr:";
|
||||
|
||||
private int database = RedisOperationsSessionRepository.DEFAULT_DATABASE;
|
||||
|
||||
/**
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
private String sessionCreatedChannelPrefix;
|
||||
|
||||
private String sessionDeletedChannel;
|
||||
|
||||
private String sessionExpiredChannel;
|
||||
|
||||
private final RedisOperations<Object, Object> sessionRedisOperations;
|
||||
|
||||
private final RedisSessionExpirationPolicy expirationPolicy;
|
||||
@@ -327,6 +340,7 @@ public class RedisOperationsSessionRepository implements
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations,
|
||||
this::getExpirationsKey, this::getSessionKey);
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,6 +391,22 @@ public class RedisOperationsSessionRepository implements
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database index to use. Defaults to {@link #DEFAULT_DATABASE}.
|
||||
* @param database the database index to use
|
||||
*/
|
||||
public void setDatabase(int database) {
|
||||
this.database = database;
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
private void configureSessionChannels() {
|
||||
this.sessionCreatedChannelPrefix = this.namespace + "event:" + this.database
|
||||
+ ":created:";
|
||||
this.sessionDeletedChannel = "__keyevent@" + this.database + "__:del";
|
||||
this.sessionExpiredChannel = "__keyevent@" + this.database + "__:expired";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RedisOperations} used for sessions.
|
||||
* @return the {@link RedisOperations} used for sessions
|
||||
@@ -502,7 +532,7 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
String channel = new String(messageChannel);
|
||||
|
||||
if (channel.startsWith(getSessionCreatedChannelPrefix())) {
|
||||
if (channel.startsWith(this.sessionCreatedChannelPrefix)) {
|
||||
// TODO: is this thread safe?
|
||||
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer
|
||||
.deserialize(message.getBody());
|
||||
@@ -515,8 +545,8 @@ public class RedisOperationsSessionRepository implements
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isDeleted = channel.endsWith(":del");
|
||||
if (isDeleted || channel.endsWith(":expired")) {
|
||||
boolean isDeleted = channel.equals(this.sessionDeletedChannel);
|
||||
if (isDeleted || channel.equals(this.sessionExpiredChannel)) {
|
||||
int beginIndex = body.lastIndexOf(":") + 1;
|
||||
int endIndex = body.length();
|
||||
String sessionId = body.substring(beginIndex, endIndex);
|
||||
@@ -579,6 +609,7 @@ public class RedisOperationsSessionRepository implements
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,17 +641,33 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private String getExpiredKeyPrefix() {
|
||||
return this.namespace + "sessions:" + "expires:";
|
||||
return this.namespace + "sessions:expires:";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prefix for the channel that SessionCreatedEvent are published to. The
|
||||
* suffix is the session id of the session that was created.
|
||||
*
|
||||
* @return the prefix for the channel that SessionCreatedEvent are published to
|
||||
* Gets the prefix for the channel that {@link SessionCreatedEvent}s are published to.
|
||||
* The suffix is the session id of the session that was created.
|
||||
* @return the prefix for the channel that {@link SessionCreatedEvent}s are published
|
||||
* to
|
||||
*/
|
||||
public String getSessionCreatedChannelPrefix() {
|
||||
return this.namespace + "event:created:";
|
||||
return this.sessionCreatedChannelPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the channel that {@link SessionDeletedEvent}s are published to.
|
||||
* @return the name for the channel that {@link SessionDeletedEvent}s are published to
|
||||
*/
|
||||
public String getSessionDeletedChannel() {
|
||||
return this.sessionDeletedChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the channel that {@link SessionExpiredEvent}s are published to.
|
||||
* @return the name for the channel that {@link SessionExpiredEvent}s are published to
|
||||
*/
|
||||
public String getSessionExpiredChannel() {
|
||||
return this.sessionExpiredChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,10 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
@@ -54,6 +57,7 @@ import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
@@ -115,6 +119,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setRedisFlushMode(this.redisFlushMode);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -128,9 +134,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
if (this.redisSubscriptionExecutor != null) {
|
||||
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
||||
}
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Arrays.asList(new PatternTopic("__keyevent@*:del"),
|
||||
new PatternTopic("__keyevent@*:expired")));
|
||||
container.addMessageListener(sessionRepository(), Arrays.asList(
|
||||
new ChannelTopic(sessionRepository().getSessionDeletedChannel()),
|
||||
new ChannelTopic(sessionRepository().getSessionExpiredChannel())));
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Collections.singletonList(new PatternTopic(
|
||||
sessionRepository().getSessionCreatedChannelPrefix() + "*")));
|
||||
@@ -256,6 +262,18 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
private int resolveDatabase() {
|
||||
if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null)
|
||||
&& this.redisConnectionFactory instanceof LettuceConnectionFactory) {
|
||||
return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
}
|
||||
if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null)
|
||||
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
||||
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
}
|
||||
return RedisOperationsSessionRepository.DEFAULT_DATABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that Redis is configured to send keyspace notifications. This is important
|
||||
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.
|
||||
|
||||
@@ -346,12 +346,16 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
}).verifyComplete();
|
||||
assertThat(
|
||||
session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -431,12 +432,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -497,9 +498,11 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
RedisSession session = sessionIdToSessions.get(sessionId);
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.getId()).isEqualTo(sessionId);
|
||||
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessed);
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(lastAccessed.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactive);
|
||||
assertThat(session.getCreationTime()).isEqualTo(createdTime);
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(createdTime.truncatedTo(ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -522,14 +525,15 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageCreated() throws Exception {
|
||||
public void onMessageCreated() {
|
||||
MapSession session = this.cached;
|
||||
byte[] pattern = "".getBytes("UTF-8");
|
||||
String channel = "spring:session:event:created:" + session.getId();
|
||||
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
JdkSerializationRedisSerializer defaultSerailizer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setDefaultSerializer(defaultSerailizer);
|
||||
byte[] body = defaultSerailizer.serialize(new HashMap());
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
|
||||
@@ -539,16 +543,16 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
assertThat(this.event.getValue().getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
// gh-309
|
||||
@Test
|
||||
public void onMessageCreatedCustomSerializer() throws Exception {
|
||||
@Test // gh-309
|
||||
public void onMessageCreatedCustomSerializer() {
|
||||
MapSession session = this.cached;
|
||||
byte[] pattern = "".getBytes("UTF-8");
|
||||
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] body = new byte[0];
|
||||
String channel = "spring:session:event:created:" + session.getId();
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
given(this.defaultSerializer.deserialize(body))
|
||||
.willReturn(new HashMap<String, Object>());
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
|
||||
this.redisRepository.onMessage(message, pattern);
|
||||
@@ -559,7 +563,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedSessionFound() throws Exception {
|
||||
public void onMessageDeletedSessionFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -570,10 +574,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:del";
|
||||
String body = "spring:session:sessions:expires:" + deletedId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -586,7 +592,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedSessionNotFound() throws Exception {
|
||||
public void onMessageDeletedSessionNotFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -594,10 +600,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:del";
|
||||
String body = "spring:session:sessions:expires:" + deletedId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -608,7 +616,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredSessionFound() throws Exception {
|
||||
public void onMessageExpiredSessionFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -619,10 +627,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:expired";
|
||||
String body = "spring:session:sessions:expires:" + expiredId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -635,7 +645,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredSessionNotFound() throws Exception {
|
||||
public void onMessageExpiredSessionNotFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -643,10 +653,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:expired";
|
||||
String body = "spring:session:sessions:expires:" + expiredId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -881,6 +893,62 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageCreatedInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "spring:session:event:created:1:" + session.getId();
|
||||
byte[] body = serializer.serialize(new HashMap());
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "__keyevent@1__:del";
|
||||
String body = "spring:session:sessions:expires:" + session.getId();
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "__keyevent@1__:expired";
|
||||
String body = "spring:session:sessions:expires:" + session.getId();
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
private String getKey(String id) {
|
||||
return "spring:session:sessions:" + id;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ apply plugin: 'io.spring.convention.spring-module'
|
||||
dependencies {
|
||||
compile project(':spring-session-core')
|
||||
compile "com.hazelcast:hazelcast"
|
||||
compile "javax.annotation:javax.annotation-api"
|
||||
compile "org.springframework:spring-context"
|
||||
|
||||
testCompile "javax.servlet:javax.servlet-api"
|
||||
|
||||
@@ -48,7 +48,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
public class HazelcastClientRepositoryITests extends AbstractHazelcastRepositoryITests {
|
||||
|
||||
private static GenericContainer container = new GenericContainer<>(
|
||||
"hazelcast/hazelcast:3.10.4")
|
||||
"hazelcast/hazelcast:3.10.6")
|
||||
.withExposedPorts(5701)
|
||||
.withEnv("JAVA_OPTS",
|
||||
"-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hazelcast.core.Offloadable;
|
||||
import com.hazelcast.map.AbstractEntryProcessor;
|
||||
import com.hazelcast.map.EntryProcessor;
|
||||
|
||||
@@ -32,7 +33,8 @@ import org.springframework.session.MapSession;
|
||||
* @since 2.0.5
|
||||
* @see HazelcastSessionRepository#save(HazelcastSessionRepository.HazelcastSession)
|
||||
*/
|
||||
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession> {
|
||||
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession>
|
||||
implements Offloadable {
|
||||
|
||||
private Instant lastAccessedTime;
|
||||
|
||||
@@ -66,6 +68,11 @@ class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSess
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutorName() {
|
||||
return OFFLOADABLE_EXECUTOR;
|
||||
}
|
||||
|
||||
void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.lastAccessedTime = lastAccessedTime;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ dependencies {
|
||||
|
||||
integrationTestCompile "com.h2database:h2"
|
||||
integrationTestCompile "com.microsoft.sqlserver:mssql-jdbc"
|
||||
integrationTestCompile "com.zaxxer:HikariCP"
|
||||
integrationTestCompile "mysql:mysql-connector-java"
|
||||
integrationTestCompile "org.apache.derby:derby"
|
||||
integrationTestCompile "org.hsqldb:hsqldb"
|
||||
@@ -21,5 +22,6 @@ dependencies {
|
||||
integrationTestCompile "org.testcontainers:mariadb"
|
||||
integrationTestCompile "org.testcontainers:mssqlserver"
|
||||
integrationTestCompile "org.testcontainers:mysql"
|
||||
integrationTestCompile "org.testcontainers:oracle-xe"
|
||||
integrationTestCompile "org.testcontainers:postgresql"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
|
||||
/**
|
||||
* Abstract base class for Testcontainers based {@link JdbcOperationsSessionRepository}
|
||||
* integration tests.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public abstract class AbstractContainerJdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
static class BaseContainerConfig extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
public HikariDataSource dataSource(JdbcDatabaseContainer databaseContainer) {
|
||||
HikariDataSource dataSource = new HikariDataSource();
|
||||
dataSource.setJdbcUrl(databaseContainer.getJdbcUrl());
|
||||
dataSource.setUsername(databaseContainer.getUsername());
|
||||
dataSource.setPassword(databaseContainer.getPassword());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer dataSourceInitializer(DataSource dataSource,
|
||||
DatabasePopulator databasePopulator) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(databasePopulator);
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -38,6 +39,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -47,7 +49,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Abstract base class for {@link JdbcOperationsSessionRepository} integration tests.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@@ -63,7 +64,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
private SecurityContext changedContext;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
public void setUp() {
|
||||
this.context = SecurityContextHolder.createEmptyContext();
|
||||
this.context.setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
|
||||
@@ -76,12 +77,13 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenNoAttributesThenCanBeFound() throws Exception {
|
||||
public void saveWhenNoAttributesThenCanBeFound() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
|
||||
this.repository.save(toSave);
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.findById(toSave.getId());
|
||||
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.isChanged()).isFalse();
|
||||
@@ -89,7 +91,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saves() throws InterruptedException {
|
||||
public void saves() {
|
||||
String username = "saves-" + System.currentTimeMillis();
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
@@ -106,7 +108,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.findById(toSave.getId());
|
||||
|
||||
assertThat(session.getId()).isEqualTo(toSave.getId());
|
||||
assertThat(session.isChanged()).isFalse();
|
||||
@@ -143,7 +146,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
this.repository.save(toSave);
|
||||
toSave = this.repository.findById(toSave.getId());
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.findById(toSave.getId());
|
||||
assertThat(session.isChanged()).isFalse();
|
||||
assertThat(session.getDelta()).isEmpty();
|
||||
assertThat(session.getAttributeNames().size()).isEqualTo(2);
|
||||
@@ -157,8 +161,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
public void updateLastAccessedTime() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setLastAccessedTime(Instant.now().minusSeconds(
|
||||
MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
toSave.setLastAccessedTime(Instant.now()
|
||||
.minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
@@ -166,17 +170,19 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
toSave.setLastAccessedTime(lastAccessedTime);
|
||||
this.repository.save(toSave);
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.findById(toSave.getId());
|
||||
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.isChanged()).isFalse();
|
||||
assertThat(session.getDelta()).isEmpty();
|
||||
assertThat(session.isExpired()).isFalse();
|
||||
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessedTime);
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(lastAccessedTime.truncatedTo(ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalName() throws Exception {
|
||||
public void findByPrincipalName() {
|
||||
String principalName = "findByPrincipalName" + UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
@@ -200,14 +206,14 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
|
||||
public void findByPrincipalNameExpireRemovesIndex() {
|
||||
String principalName = "findByPrincipalNameExpireRemovesIndex"
|
||||
+ UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
toSave.setLastAccessedTime(Instant.now().minusSeconds(
|
||||
MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
toSave.setLastAccessedTime(Instant.now()
|
||||
.minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
|
||||
this.repository.save(toSave);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
@@ -220,7 +226,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
|
||||
public void findByPrincipalNameNoPrincipalNameChange() {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChange"
|
||||
+ UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
@@ -244,7 +250,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
|
||||
public void findByPrincipalNameNoPrincipalNameChangeReload() {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
|
||||
+ UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
@@ -270,7 +276,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedPrincipalName() throws Exception {
|
||||
public void findByDeletedPrincipalName() {
|
||||
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
@@ -288,7 +294,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedPrincipalName() throws Exception {
|
||||
public void findByChangedPrincipalName() {
|
||||
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
@@ -316,7 +322,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedPrincipalNameReload() throws Exception {
|
||||
public void findByDeletedPrincipalNameReload() {
|
||||
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
@@ -336,7 +342,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedPrincipalNameReload() throws Exception {
|
||||
public void findByChangedPrincipalNameReload() {
|
||||
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
@@ -367,7 +373,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBySecurityPrincipalName() throws Exception {
|
||||
public void findBySecurityPrincipalName() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -390,12 +396,12 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
|
||||
public void findBySecurityPrincipalNameExpireRemovesIndex() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
toSave.setLastAccessedTime(Instant.now().minusSeconds(
|
||||
MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
toSave.setLastAccessedTime(Instant.now()
|
||||
.minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
|
||||
|
||||
this.repository.save(toSave);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
@@ -408,7 +414,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChange() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -430,8 +436,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
|
||||
throws Exception {
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -455,7 +460,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedSecurityPrincipalName() throws Exception {
|
||||
public void findByDeletedSecurityPrincipalName() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -472,7 +477,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedSecurityPrincipalName() throws Exception {
|
||||
public void findByChangedSecurityPrincipalName() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -498,7 +503,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
|
||||
public void findByDeletedSecurityPrincipalNameReload() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -517,7 +522,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedSecurityPrincipalNameReload() throws Exception {
|
||||
public void findByChangedSecurityPrincipalNameReload() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
@@ -604,15 +609,17 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenOnlyChangeId() throws Exception {
|
||||
public void changeSessionIdWhenOnlyChangeId() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
toSave.setAttribute(attrName, attrValue);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession findById = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession findById = this.repository
|
||||
.findById(toSave.getId());
|
||||
|
||||
assertThat(findById.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
|
||||
@@ -623,16 +630,19 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
assertThat(this.repository.findById(originalFindById)).isNull();
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession findByChangeSessionId = this.repository.findById(changeSessionId);
|
||||
JdbcOperationsSessionRepository.JdbcSession findByChangeSessionId = this.repository
|
||||
.findById(changeSessionId);
|
||||
|
||||
assertThat(findByChangeSessionId.isChanged()).isFalse();
|
||||
assertThat(findByChangeSessionId.getDelta()).isEmpty();
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName))
|
||||
.isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenChangeTwice() throws Exception {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
|
||||
public void changeSessionIdWhenChangeTwice() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
@@ -648,15 +658,17 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSetAttributeOnChangedSession() throws Exception {
|
||||
public void changeSessionIdWhenSetAttributeOnChangedSession() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession findById = this.repository.findById(toSave.getId());
|
||||
JdbcOperationsSessionRepository.JdbcSession findById = this.repository
|
||||
.findById(toSave.getId());
|
||||
|
||||
findById.setAttribute(attrName, attrValue);
|
||||
|
||||
@@ -667,19 +679,19 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
assertThat(this.repository.findById(originalFindById)).isNull();
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession findByChangeSessionId = this.repository.findById(changeSessionId);
|
||||
JdbcOperationsSessionRepository.JdbcSession findByChangeSessionId = this.repository
|
||||
.findById(changeSessionId);
|
||||
|
||||
assertThat(findByChangeSessionId.isChanged()).isFalse();
|
||||
assertThat(findByChangeSessionId.getDelta()).isEmpty();
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName))
|
||||
.isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenHasNotSaved() throws Exception {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
|
||||
public void changeSessionIdWhenHasNotSaved() {
|
||||
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
|
||||
.createSession();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
|
||||
@@ -691,7 +703,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1070
|
||||
public void saveUpdatedAddAndModifyAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
session.setAttribute("testName", "testValue1");
|
||||
@@ -704,7 +717,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1070
|
||||
public void saveUpdatedAddAndRemoveAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
session.setAttribute("testName", "testValue");
|
||||
@@ -717,7 +731,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1070
|
||||
public void saveUpdatedModifyAndRemoveAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setAttribute("testName", "testValue1");
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
@@ -731,7 +746,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1070
|
||||
public void saveUpdatedRemoveAndAddAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setAttribute("testName", "testValue1");
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
@@ -745,7 +761,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1031
|
||||
public void saveDeleted() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
this.repository.deleteById(session.getId());
|
||||
@@ -757,7 +774,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Test // gh-1031
|
||||
public void saveDeletedAddAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
this.repository.deleteById(session.getId());
|
||||
@@ -768,6 +786,46 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
assertThat(this.repository.findById(session.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // gh-1133
|
||||
public void sessionFromStoreResolvesAttributesLazily() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
MapSession delegate = (MapSession) ReflectionTestUtils.getField(session,
|
||||
"delegate");
|
||||
|
||||
assertThat((String) session.getAttribute("attribute1")).isEqualTo("value1");
|
||||
assertThat(delegate).isNotNull();
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute1"), "value"))
|
||||
.isEqualTo("value1");
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute2"), "value"))
|
||||
.isNull();
|
||||
assertThat((String) session.getAttribute("attribute2")).isEqualTo("value2");
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute2"), "value"))
|
||||
.isEqualTo("value2");
|
||||
}
|
||||
|
||||
@Test // gh-1203
|
||||
public void saveWithLargeAttribute() {
|
||||
String attributeName = "largeAttribute";
|
||||
int arraySize = 4000;
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setAttribute(attributeName, new byte[arraySize]);
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
|
||||
assertThat(session).isNotNull();
|
||||
assertThat((byte[]) session.getAttribute(attributeName)).hasSize(arraySize);
|
||||
}
|
||||
|
||||
private String getSecurityName() {
|
||||
return this.context.getAuthentication().getName();
|
||||
}
|
||||
@@ -777,7 +835,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
@EnableJdbcHttpSession
|
||||
protected static class BaseConfig {
|
||||
static class BaseConfig {
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(DataSource dataSource) {
|
||||
@@ -785,4 +843,5 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.session.jdbc;
|
||||
|
||||
import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
import org.testcontainers.containers.MSSQLServerContainer;
|
||||
import org.testcontainers.containers.MariaDBContainer;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
import org.testcontainers.containers.OracleContainer;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
/**
|
||||
* Factories for various {@link JdbcDatabaseContainer}s.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
final class DatabaseContainers {
|
||||
|
||||
private DatabaseContainers() {
|
||||
}
|
||||
|
||||
static MariaDBContainer mariaDb5() {
|
||||
return new MariaDb5Container();
|
||||
}
|
||||
|
||||
static MariaDBContainer mariaDb10() {
|
||||
return new MariaDb10Container();
|
||||
}
|
||||
|
||||
static MySQLContainer mySql5() {
|
||||
return new MySql5Container();
|
||||
}
|
||||
|
||||
static MySQLContainer mySql8() {
|
||||
return new MySql8Container();
|
||||
}
|
||||
|
||||
static OracleContainer oracle() {
|
||||
return new OracleContainer();
|
||||
}
|
||||
|
||||
static PostgreSQLContainer postgreSql9() {
|
||||
return new PostgreSql9Container();
|
||||
}
|
||||
|
||||
static PostgreSQLContainer postgreSql10() {
|
||||
return new PostgreSql10Container();
|
||||
}
|
||||
|
||||
static MSSQLServerContainer sqlServer2017() {
|
||||
return new SqlServer2017Container();
|
||||
}
|
||||
|
||||
private static class MariaDb5Container extends MariaDBContainer<MariaDb5Container> {
|
||||
|
||||
MariaDb5Container() {
|
||||
super("mariadb:5.5.61");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci", "--innodb_large_prefix",
|
||||
"--innodb_file_format=barracuda", "--innodb-file-per-table");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MariaDb10Container extends MariaDBContainer<MariaDb10Container> {
|
||||
|
||||
MariaDb10Container() {
|
||||
super("mariadb:10.3.10");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MySql5Container extends MySQLContainer<MySql5Container> {
|
||||
|
||||
MySql5Container() {
|
||||
super("mysql:5.7.23");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return "com.mysql.cj.jdbc.Driver";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MySql8Container extends MySQLContainer<MySql8Container> {
|
||||
|
||||
MySql8Container() {
|
||||
super("mysql:8.0.12");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--default-authentication-plugin=mysql_native_password");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return "com.mysql.cj.jdbc.Driver";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PostgreSql9Container
|
||||
extends PostgreSQLContainer<PostgreSql9Container> {
|
||||
|
||||
PostgreSql9Container() {
|
||||
super("postgres:9.6.10");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PostgreSql10Container
|
||||
extends PostgreSQLContainer<PostgreSql10Container> {
|
||||
|
||||
PostgreSql10Container() {
|
||||
super("postgres:10.5");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SqlServer2017Container
|
||||
extends MSSQLServerContainer<SqlServer2017Container> {
|
||||
|
||||
SqlServer2017Container() {
|
||||
super("microsoft/mssql-server-linux:2017-CU11");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
withStartupTimeoutSeconds(240);
|
||||
withConnectTimeoutSeconds(240);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.session.jdbc;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
/**
|
||||
* Factories for various {@link DatabasePopulator}s.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
final class DatabasePopulators {
|
||||
|
||||
private DatabasePopulators() {
|
||||
}
|
||||
|
||||
static ResourceDatabasePopulator mySql() {
|
||||
return new ResourceDatabasePopulator(new ClassPathResource(
|
||||
"org/springframework/session/jdbc/schema-mysql.sql"));
|
||||
}
|
||||
|
||||
static ResourceDatabasePopulator oracle() {
|
||||
return new ResourceDatabasePopulator(new ClassPathResource(
|
||||
"org/springframework/session/jdbc/schema-oracle.sql"));
|
||||
}
|
||||
|
||||
static ResourceDatabasePopulator postgreSql() {
|
||||
return new ResourceDatabasePopulator(new ClassPathResource(
|
||||
"org/springframework/session/jdbc/schema-postgresql.sql"));
|
||||
}
|
||||
|
||||
static ResourceDatabasePopulator sqlServer() {
|
||||
return new ResourceDatabasePopulator(new ClassPathResource(
|
||||
"org/springframework/session/jdbc/schema-sqlserver.sql"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,20 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mariadb.jdbc.MariaDbDataSource;
|
||||
import org.testcontainers.containers.MariaDBContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -45,55 +36,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class MariaDb10JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static MariaDBContainer container = new MariaDb10Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() throws SQLException {
|
||||
MariaDbDataSource dataSource = new MariaDbDataSource(container.getJdbcUrl());
|
||||
dataSource.setUserName(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public MariaDBContainer databaseContainer() {
|
||||
MariaDBContainer databaseContainer = DatabaseContainers.mariaDb10();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-mysql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MariaDb10Container extends MariaDBContainer<MariaDb10Container> {
|
||||
|
||||
MariaDb10Container() {
|
||||
super("mariadb:10.3.9");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci");
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.mySql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,20 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mariadb.jdbc.MariaDbDataSource;
|
||||
import org.testcontainers.containers.MariaDBContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -45,56 +36,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class MariaDb5JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static MariaDBContainer container = new MariaDb5Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() throws SQLException {
|
||||
MariaDbDataSource dataSource = new MariaDbDataSource(container.getJdbcUrl());
|
||||
dataSource.setUserName(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public MariaDBContainer databaseContainer() {
|
||||
MariaDBContainer databaseContainer = DatabaseContainers.mariaDb5();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-mysql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MariaDb5Container extends MariaDBContainer<MariaDb5Container> {
|
||||
|
||||
MariaDb5Container() {
|
||||
super("mariadb:5.5.61");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci", "--innodb_large_prefix",
|
||||
"--innodb_file_format=barracuda", "--innodb-file-per-table");
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.mySql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,19 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
import com.mysql.cj.jdbc.MysqlDataSource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,61 +35,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class MySql5JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static MySQLContainer container = new MySql5Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
dataSource.setUrl(container.getJdbcUrl());
|
||||
dataSource.setUser(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public MySQLContainer databaseContainer() {
|
||||
MySQLContainer databaseContainer = DatabaseContainers.mySql5();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-mysql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MySql5Container extends MySQLContainer<MySql5Container> {
|
||||
|
||||
MySql5Container() {
|
||||
super("mysql:5.7.23");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return Driver.class.getName();
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.mySql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,19 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
import com.mysql.cj.jdbc.MysqlDataSource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,60 +35,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class MySql8JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static MySQLContainer container = new MySql8Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
dataSource.setUrl(container.getJdbcUrl());
|
||||
dataSource.setUser(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public MySQLContainer databaseContainer() {
|
||||
MySQLContainer databaseContainer = DatabaseContainers.mySql8();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-mysql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MySql8Container extends MySQLContainer<MySql8Container> {
|
||||
|
||||
MySql8Container() {
|
||||
super("mysql:8.0.12");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
setCommand("mysqld", "--default-authentication-plugin=mysql_native_password");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDriverClassName() {
|
||||
return Driver.class.getName();
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.mySql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.session.jdbc;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.testcontainers.containers.OracleContainer;
|
||||
import org.testcontainers.utility.TestcontainersConfiguration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link JdbcOperationsSessionRepository} using Oracle database.
|
||||
* <p>
|
||||
* This test is conditional on presence of Oracle JDBC driver on the classpath and
|
||||
* Testcontainers property {@code oracle.container.image} being set.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class OracleJdbcOperationsSessionRepositoryITests
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
Assume.assumeTrue("Oracle JDBC driver is present on the classpath",
|
||||
ClassUtils.isPresent("oracle.jdbc.OracleDriver", null));
|
||||
Assume.assumeTrue("Testcontainers property `oracle.container.image` is set",
|
||||
TestcontainersConfiguration.getInstance().getProperties()
|
||||
.getProperty("oracle.container.image") != null);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public OracleContainer databaseContainer() {
|
||||
OracleContainer databaseContainer = DatabaseContainers.oracle();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.oracle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,18 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.postgresql.ds.PGSimpleDataSource;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,50 +36,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class PostgreSql10JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static PostgreSQLContainer container = new PostgreSql10Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
PGSimpleDataSource dataSource = new PGSimpleDataSource();
|
||||
dataSource.setUrl(container.getJdbcUrl());
|
||||
dataSource.setUser(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public PostgreSQLContainer databaseContainer() {
|
||||
PostgreSQLContainer databaseContainer = DatabaseContainers.postgreSql10();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-postgresql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PostgreSql10Container
|
||||
extends PostgreSQLContainer<PostgreSql10Container> {
|
||||
|
||||
PostgreSql10Container() {
|
||||
super("postgres:10.5");
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.postgreSql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,18 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.postgresql.ds.PGSimpleDataSource;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,50 +36,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class PostgreSql9JdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static PostgreSQLContainer container = new PostgreSql9Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
PGSimpleDataSource dataSource = new PGSimpleDataSource();
|
||||
dataSource.setUrl(container.getJdbcUrl());
|
||||
dataSource.setUser(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public PostgreSQLContainer databaseContainer() {
|
||||
PostgreSQLContainer databaseContainer = DatabaseContainers.postgreSql9();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-postgresql.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PostgreSql9Container
|
||||
extends PostgreSQLContainer<PostgreSql9Container> {
|
||||
|
||||
PostgreSql9Container() {
|
||||
super("postgres:9.6.10");
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.postgreSql();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,18 +16,11 @@
|
||||
|
||||
package org.springframework.session.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.testcontainers.containers.MSSQLServerContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,52 +36,21 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class SqlServerJdbcOperationsSessionRepositoryITests
|
||||
extends AbstractJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
private static MSSQLServerContainer container = new SqlServer2007Container();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
container.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
container.stop();
|
||||
}
|
||||
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
|
||||
|
||||
@Configuration
|
||||
static class Config extends BaseConfig {
|
||||
static class Config extends BaseContainerConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
SQLServerDataSource dataSource = new SQLServerDataSource();
|
||||
dataSource.setURL(container.getJdbcUrl());
|
||||
dataSource.setUser(container.getUsername());
|
||||
dataSource.setPassword(container.getPassword());
|
||||
return dataSource;
|
||||
public MSSQLServerContainer databaseContainer() {
|
||||
MSSQLServerContainer databaseContainer = DatabaseContainers.sqlServer2017();
|
||||
databaseContainer.start();
|
||||
return databaseContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceInitializer initializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.setDatabasePopulator(
|
||||
new ResourceDatabasePopulator(resourceLoader.getResource(
|
||||
"classpath:org/springframework/session/jdbc/schema-sqlserver.sql")));
|
||||
return initializer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SqlServer2007Container
|
||||
extends MSSQLServerContainer<SqlServer2007Container> {
|
||||
|
||||
SqlServer2007Container() {
|
||||
super("microsoft/mssql-server-linux:2017-CU9");
|
||||
withStartupTimeoutSeconds(240);
|
||||
withConnectTimeoutSeconds(240);
|
||||
public ResourceDatabasePopulator databasePopulator() {
|
||||
return DatabasePopulators.sqlServer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
microsoft/mssql-server-linux:2017-CU9
|
||||
microsoft/mssql-server-linux:2017-CU11
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -28,6 +28,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -128,6 +129,7 @@ import org.springframework.util.StringUtils;
|
||||
* target database type.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Craig Andrews
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class JdbcOperationsSessionRepository implements
|
||||
@@ -530,7 +532,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||||
String attributeName = attributeNames.get(i);
|
||||
ps.setString(1, attributeName);
|
||||
serialize(ps, 2, session.getAttribute(attributeName));
|
||||
setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
|
||||
ps.setString(3, session.getId());
|
||||
}
|
||||
|
||||
@@ -545,7 +547,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
this.jdbcOperations.update(this.createSessionAttributeQuery, (ps) -> {
|
||||
String attributeName = attributeNames.get(0);
|
||||
ps.setString(1, attributeName);
|
||||
serialize(ps, 2, session.getAttribute(attributeName));
|
||||
setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
|
||||
ps.setString(3, session.getId());
|
||||
});
|
||||
}
|
||||
@@ -559,7 +561,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
@Override
|
||||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||||
String attributeName = attributeNames.get(i);
|
||||
serialize(ps, 1, session.getAttribute(attributeName));
|
||||
setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
|
||||
ps.setString(2, session.primaryKey);
|
||||
ps.setString(3, attributeName);
|
||||
}
|
||||
@@ -574,7 +576,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
else {
|
||||
this.jdbcOperations.update(this.updateSessionAttributeQuery, (ps) -> {
|
||||
String attributeName = attributeNames.get(0);
|
||||
serialize(ps, 1, session.getAttribute(attributeName));
|
||||
setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
|
||||
ps.setString(2, session.primaryKey);
|
||||
ps.setString(3, attributeName);
|
||||
});
|
||||
@@ -657,19 +659,17 @@ public class JdbcOperationsSessionRepository implements
|
||||
getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY);
|
||||
}
|
||||
|
||||
private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
|
||||
private void setObjectAsBlob(PreparedStatement ps, int paramIndex, Object object)
|
||||
throws SQLException {
|
||||
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex,
|
||||
(byte[]) this.conversionService.convert(attributeValue,
|
||||
TypeDescriptor.valueOf(Object.class),
|
||||
TypeDescriptor.valueOf(byte[].class)));
|
||||
byte[] bytes = (byte[]) this.conversionService.convert(object,
|
||||
TypeDescriptor.valueOf(Object.class),
|
||||
TypeDescriptor.valueOf(byte[].class));
|
||||
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex, bytes);
|
||||
}
|
||||
|
||||
private Object deserialize(ResultSet rs, String columnName)
|
||||
throws SQLException {
|
||||
return this.conversionService.convert(
|
||||
this.lobHandler.getBlobAsBytes(rs, columnName),
|
||||
TypeDescriptor.valueOf(byte[].class),
|
||||
private Object getBlobAsObject(ResultSet rs, String columnName) throws SQLException {
|
||||
byte[] bytes = this.lobHandler.getBlobAsBytes(rs, columnName);
|
||||
return this.conversionService.convert(bytes, TypeDescriptor.valueOf(byte[].class),
|
||||
TypeDescriptor.valueOf(Object.class));
|
||||
}
|
||||
|
||||
@@ -679,6 +679,28 @@ public class JdbcOperationsSessionRepository implements
|
||||
|
||||
}
|
||||
|
||||
private static <T> Supplier<T> value(T value) {
|
||||
return (value != null) ? () -> value : null;
|
||||
}
|
||||
|
||||
private static <T> Supplier<T> lazily(Supplier<T> supplier) {
|
||||
Supplier<T> lazySupplier = new Supplier<T>() {
|
||||
|
||||
private T value;
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
if (this.value == null) {
|
||||
this.value = supplier.get();
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (supplier != null) ? lazySupplier : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Session} to use for {@link JdbcOperationsSessionRepository}.
|
||||
*
|
||||
@@ -748,7 +770,8 @@ public class JdbcOperationsSessionRepository implements
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.delegate.getAttribute(attributeName);
|
||||
Supplier<T> supplier = this.delegate.getAttribute(attributeName);
|
||||
return (supplier != null) ? supplier.get() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -783,7 +806,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
? oldDeltaValue
|
||||
: DeltaValue.UPDATED);
|
||||
}
|
||||
this.delegate.setAttribute(attributeName, attributeValue);
|
||||
this.delegate.setAttribute(attributeName, value(attributeValue));
|
||||
if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||
|
||||
SPRING_SECURITY_CONTEXT.equals(attributeName)) {
|
||||
this.changed = true;
|
||||
@@ -875,7 +898,8 @@ public class JdbcOperationsSessionRepository implements
|
||||
}
|
||||
String attributeName = rs.getString("ATTRIBUTE_NAME");
|
||||
if (attributeName != null) {
|
||||
session.delegate.setAttribute(attributeName, deserialize(rs, "ATTRIBUTE_BYTES"));
|
||||
Object attributeValue = getBlobAsObject(rs, "ATTRIBUTE_BYTES");
|
||||
session.delegate.setAttribute(attributeName, lazily(() -> attributeValue));
|
||||
}
|
||||
sessions.add(session);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -35,6 +35,9 @@ import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
@@ -102,6 +105,11 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
if (this.lobHandler != null) {
|
||||
sessionRepository.setLobHandler(this.lobHandler);
|
||||
}
|
||||
else if (requiresTemporaryLob(this.dataSource)) {
|
||||
DefaultLobHandler lobHandler = new DefaultLobHandler();
|
||||
lobHandler.setCreateTemporaryLob(true);
|
||||
sessionRepository.setLobHandler(lobHandler);
|
||||
}
|
||||
if (this.springSessionConversionService != null) {
|
||||
sessionRepository.setConversionService(this.springSessionConversionService);
|
||||
}
|
||||
@@ -115,6 +123,17 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
private static boolean requiresTemporaryLob(DataSource dataSource) {
|
||||
try {
|
||||
String productName = JdbcUtils.extractDatabaseMetaData(dataSource,
|
||||
"getDatabaseProductName");
|
||||
return "Oracle".equalsIgnoreCase(JdbcUtils.commonDatabaseName(productName));
|
||||
}
|
||||
catch (MetaDataAccessException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user