Compare commits

..

6 Commits

Author SHA1 Message Date
Vedran Pavic
d7c2e8e79c Release 2.0.5.RELEASE 2018-07-29 09:48:45 +02:00
Vedran Pavic
6bec95a298 Polish 2018-07-27 13:27:27 +02:00
Vedran Pavic
9249a140c9 Upgrade dependencies 2018-07-27 11:05:33 +02:00
Vedran Pavic
7f6dc801e0 Upgrade Spring Data to Kay-SR9
Closes gh-1122
2018-07-27 10:23:12 +02:00
Vedran Pavic
83d46ad685 Upgrade Spring Security to 5.0.7.RELEASE
Closes gh-1123
2018-07-27 01:14:21 +02:00
Vedran Pavic
21cef2b7fa Upgrade Spring Framework to 5.0.8.RELEASE
Closes gh-1121
2018-07-26 23:25:13 +02:00
105 changed files with 801 additions and 1847 deletions

127
Jenkinsfile vendored
View File

@@ -1,9 +1,9 @@
properties([
buildDiscarder(logRotator(numToKeepStr: '10')),
pipelineTriggers([
cron('@daily')
]),
])
def projectProperties = [
[$class: 'BuildDiscarderProperty',
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
pipelineTriggers([cron('@daily')])
]
properties(projectProperties)
def SUCCESS = hudson.model.Result.SUCCESS.toString()
currentBuild.result = SUCCESS
@@ -11,80 +11,49 @@ currentBuild.result = SUCCESS
try {
parallel check: {
stage('Check') {
timeout(time: 45, unit: 'MINUTES') {
node('ubuntu1804') {
checkout scm
try {
sh './gradlew clean check --no-daemon --refresh-dependencies'
}
catch (e) {
currentBuild.result = 'FAILED: check'
throw e
}
finally {
junit '**/build/test-results/*/*.xml'
}
node {
checkout scm
try {
sh "./gradlew clean check --refresh-dependencies --no-daemon"
} catch(Exception e) {
currentBuild.result = 'FAILED: check'
throw e
} finally {
junit '**/build/*-results/*.xml'
}
}
}
},
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
}
springio: {
stage('Spring IO') {
node {
checkout scm
try {
sh "./gradlew clean springIoCheck -PplatformVersion=Cairo-BUILD-SNAPSHOT -PexcludeProjects='**/samples/**' --refresh-dependencies --no-daemon --stacktrace"
} catch(Exception e) {
currentBuild.result = 'FAILED: springio'
throw e
} finally {
junit '**/build/spring-io*-results/*.xml'
}
}
}
}
if (currentBuild.result == 'SUCCESS') {
if(currentBuild.result == 'SUCCESS') {
parallel artifacts: {
stage('Deploy Artifacts') {
node {
checkout scm
try {
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
sh './gradlew deployArtifacts finalizeDeployArtifacts --stacktrace --no-daemon --refresh-dependencies -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD'
}
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
catch (e) {
currentBuild.result = 'FAILED: artifacts'
throw e
}
}
}
},
@@ -92,38 +61,32 @@ try {
stage('Deploy Docs') {
node {
checkout scm
try {
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh './gradlew deployDocs --stacktrace --no-daemon --refresh-dependencies -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME'
}
}
catch (e) {
currentBuild.result = 'FAILED: docs'
throw e
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
}
}
finally {
} finally {
def buildStatus = currentBuild.result
def buildNotSuccess = !SUCCESS.equals(buildStatus)
def buildNotSuccess = !SUCCESS.equals(buildStatus)
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
if (buildNotSuccess || lastBuildNotSuccess) {
stage('Notify') {
if(buildNotSuccess || lastBuildNotSuccess) {
stage('Notifiy') {
node {
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
def details = "The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
emailext(
subject: subject,
body: details,
recipientProviders: RECIPIENTS,
to: "$SPRING_SESSION_TEAM_EMAILS"
emailext (
subject: subject,
body: details,
recipientProviders: RECIPIENTS,
to: "$SPRING_SESSION_TEAM_EMAILS"
)
}
}

View File

@@ -1,38 +1,20 @@
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.19.RELEASE'
classpath 'io.spring.gradle:spring-build-conventions:0.0.17.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'
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
}
}
}
}
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.milestoneBuild = !(releaseBuild || snapshotBuild)

View File

@@ -614,10 +614,9 @@ Spring Session's most basic API for using a `Session` is the `SessionRepository`
This API is intentionally very simple, so that it is easy to provide additional implementations with basic functionality.
Some `SessionRepository` implementations may choose to implement `FindByIndexNameSessionRepository` also.
For example, Spring's Redis, JDBC and Hazelcast support all implement `FindByIndexNameSessionRepository`.
For example, Spring's Redis support implements `FindByIndexNameSessionRepository`.
The `FindByIndexNameSessionRepository` provides a method to look up all the sessions with a given index name and index value.
As a common use case that is supported by all provided `FindByIndexNameSessionRepository` implementations, there's a convenient method to look up all the sessions for a particular user.
The `FindByIndexNameSessionRepository` adds a single method to look up all the sessions for a particular user.
This is done by ensuring that the session attribute with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
It is the responsibility of the developer to ensure the attribute is populated since Spring Session is not aware of the authentication mechanism being used.
An example of how this might be used can be seen below:

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,7 +52,9 @@ public class FindByIndexNameSessionRepositoryTests {
// tag::findby-username[]
String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository
.findByPrincipalName(username);
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
// end::findby-username[]
}
}

View File

@@ -24,6 +24,7 @@ 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;
@@ -53,7 +54,7 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
// tag::rememberme-bean[]
@Bean
public SpringSessionRememberMeServices rememberMeServices() {
RememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
/**
* @author rwinch
* @author Vedran Pavic
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
@@ -87,6 +86,5 @@ public class RememberMeSecurityConfigurationTests<T extends Session> {
.isEqualTo(Duration.ofDays(30));
}
}
// end::class[]

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
/**
* @author rwinch
* @author Vedran Pavic
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@@ -87,6 +86,5 @@ public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
.isEqualTo(Duration.ofDays(30));
}
}
// end::class[]

View File

@@ -4,7 +4,7 @@
<module name="Checker">
<!-- Supressions -->
<module name="SuppressionFilter">
<property name="file" value="${config_loc}/suppressions.xml"/>
<property name="file" value="${configDir}/suppressions.xml"/>
</module>
<!-- Root Checks -->

16
etc/checkstyle/header.txt Normal file
View File

@@ -0,0 +1,16 @@
^\Q/*\E$
^\Q * Copyright 2014-\E20\d\d\Q the original author or authors.\E$
^\Q *\E$
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
^\Q * you may not use this file except in compliance with the License.\E$
^\Q * You may obtain a copy of the License at\E$
^\Q *\E$
^\Q * http://www.apache.org/licenses/LICENSE-2.0\E$
^\Q *\E$
^\Q * Unless required by applicable law or agreed to in writing, software\E$
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
^\Q * See the License for the specific language governing permissions and\E$
^\Q * limitations under the License.\E$
^\Q */\E$
^.*$

View File

@@ -7,6 +7,7 @@
<!-- docs -->
<suppress files="[\\/]docs[\\/]" checks="Javadoc*"/>
<suppress files="[\\/]docs[\\/]" checks="AvoidStaticImport"/>
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
<!-- samples -->

View File

@@ -1 +1,2 @@
version=2.1.1.RELEASE
springBootVersion=2.0.3.RELEASE
version=2.0.5.RELEASE

View File

@@ -1,33 +1,31 @@
dependencyManagement {
imports {
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
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'
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR10'
mavenBom 'org.springframework:spring-framework-bom:5.0.8.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR9'
mavenBom 'org.springframework.security:spring-security-bom:5.0.7.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.8.1'
}
dependencies {
dependencySet(group: 'com.hazelcast', version: '3.10.6') {
dependencySet(group: 'com.hazelcast', version: '3.9.4') {
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 'com.microsoft.sqlserver:mssql-jdbc:6.4.0.jre8'
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
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 'io.lettuce:lettuce-core:5.0.4.RELEASE'
dependency 'javax.servlet:javax.servlet-api:3.1.0'
dependency 'junit:junit:4.12'
dependency 'mysql:mysql-connector-java:8.0.12'
dependency 'mysql:mysql-connector-java:8.0.11'
dependency 'org.apache.derby:derby:10.14.2.0'
dependency 'org.assertj:assertj-core:3.11.1'
dependency 'org.assertj:assertj-core:3.10.0'
dependency 'org.hsqldb:hsqldb:2.4.1'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.3.0'
dependency 'org.mockito:mockito-core:2.23.0'
dependency 'org.postgresql:postgresql:42.2.5'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.2.6'
dependency 'org.mockito:mockito-core:2.20.1'
dependency 'org.postgresql:postgresql:42.2.4'
}
}

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -46,7 +46,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class FindByUsernameTests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,7 +44,10 @@ public class IndexController {
@RequestMapping("/")
public String index(Principal principal, Model model) {
Collection<? extends Session> usersSessions = this.sessions
.findByPrincipalName(principal.getName()).values();
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName())
.values();
model.addAttribute("sessions", usersSessions);
return "index";
}
@@ -53,8 +56,9 @@ public class IndexController {
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
public String removeSession(Principal principal,
@PathVariable String sessionIdToDelete) {
Set<String> usersSessionIds = this.sessions
.findByPrincipalName(principal.getName()).keySet();
Set<String> usersSessionIds = this.sessions.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName()).keySet();
if (usersSessionIds.contains(sessionIdToDelete)) {
this.sessions.deleteById(sessionIdToDelete);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
/**
* @author Eddú Meléndez
* @author Vedran Pavic
*/
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
}
public Form form() {
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
@FindBy(name = "password")
private WebElement password;
@FindBy(tagName = "button")
@FindBy(name = "submit")
private WebElement button;
public Form(SearchContext context) {

View File

@@ -50,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@AutoConfigureMockMvc
public class HttpRedisJsonTest {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class RedisSerializerTest {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@SpringSessionRedisOperations
private RedisTemplate<Object, Object> sessionRedisTemplate;

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -45,7 +45,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class BootTests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
}
public Form form() {
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
@FindBy(name = "password")
private WebElement password;
@FindBy(tagName = "button")
@FindBy(name = "submit")
private WebElement button;
public Form(SearchContext context) {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class AttributeTests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@LocalServerPort
private int port;

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -23,6 +23,5 @@ dependencies {
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.springframework.security:spring-security-test"
integrationTestCompile "org.testcontainers:testcontainers"
testCompile "org.testcontainers:testcontainers"
}

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Value("${local.server.port}")
private String port;

View File

@@ -5,7 +5,7 @@ dependencyManagement {
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.32.0'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.29.3'
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
dependency 'org.webjars:bootstrap:2.3.2'

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
}
public Form form() {
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
@FindBy(name = "password")
private WebElement password;
@FindBy(tagName = "button")
@FindBy(name = "submit")
private WebElement button;
public Form(SearchContext context) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,8 +42,7 @@ public class SessionConfig {
int port = SocketUtils.findAvailableTcpPort();
config.getNetworkConfig()
.setPort(port)
.getJoin().getMulticastConfig().setEnabled(false);
.setPort(port);
System.out.println("Hazelcast port #: " + port);

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -17,6 +17,8 @@ dependencies {
testCompile "org.springframework.security:spring-security-test"
testCompile "org.assertj:assertj-core"
testCompile "org.springframework:spring-test"
integrationTestCompile "org.testcontainers:testcontainers"
}
gretty {

View File

@@ -54,7 +54,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@WebAppConfiguration
public class RestMockMvcTests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public class LoginPage extends BasePage {
@FindBy(name = "password")
private WebElement password;
@FindBy(tagName = "button")
@FindBy(css = "input[type='submit']")
private WebElement button;
public LoginPage(WebDriver driver) {
@@ -47,7 +47,7 @@ public class LoginPage extends BasePage {
}
public void assertAt() {
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
}
public HomePage login(String user, String password) {

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -64,9 +64,7 @@ public class Initializer implements ServletContextListener {
private HazelcastInstance createHazelcastInstance() {
Config config = new Config();
config.getNetworkConfig()
.setPort(getAvailablePort())
.getJoin().getMulticastConfig().setEnabled(false);
config.getNetworkConfig().setPort(getAvailablePort());
config.getMapConfig(SESSION_MAP_NAME)
.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -2,18 +2,15 @@ rootProject.name = 'spring-session-build'
FileTree buildFiles = fileTree(rootDir) {
include '**/*.gradle'
exclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'
exclude '**/grails3'
gradle.startParameter.projectProperties.get('excludeProjects')?.split(',')?.each { excludeProject ->
exclude excludeProject
}
}
String rootDirPath = rootDir.absolutePath + File.separator
buildFiles.each { buildFile ->
buildFiles.each { File buildFile ->
if (buildFile.name == 'build.gradle') {
String buildFilePath = buildFile.parentFile.absolutePath
String projectPath = buildFilePath.replace(rootDirPath, '').replace(File.separator, ':')
String projectPath = buildFilePath.replace(rootDirPath, '').replaceAll(File.separator, ':')
include projectPath
}
else {

View File

@@ -6,7 +6,6 @@ dependencies {
compile "org.springframework:spring-jcl"
optional "io.projectreactor:reactor-core"
optional "javax.annotation:javax.annotation-api"
optional "javax.servlet:javax.servlet-api"
optional "org.springframework:spring-context"
optional "org.springframework:spring-jdbc"

View File

@@ -19,22 +19,27 @@ package org.springframework.session;
import java.util.Map;
/**
* Extends a basic {@link SessionRepository} to allow finding sessions by the specified
* index name and index value.
* Extends a basic {@link SessionRepository} to allow finding a session id by the
* principal name. The principal name is defined by the {@link Session} attribute with the
* name {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}.
*
* @param <S> the type of Session being managed by this
* {@link FindByIndexNameSessionRepository}
* @author Rob Winch
* @author Vedran Pavic
*/
public interface FindByIndexNameSessionRepository<S extends Session>
extends SessionRepository<S> {
/**
* A session index that contains the current principal name (i.e. username).
* <p>
* It is the responsibility of the developer to ensure the index is populated since
* Spring Session is not aware of the authentication mechanism being used.
* A common session attribute that contains the current principal name (i.e.
* username).
* </p>
*
* <p>
* It is the responsibility of the developer to ensure the attribute is populated
* since Spring Session is not aware of the authentication mechanism being used.
* </p>
*
* @since 1.1
*/
@@ -42,34 +47,17 @@ public interface FindByIndexNameSessionRepository<S extends Session>
.concat(".PRINCIPAL_NAME_INDEX_NAME");
/**
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
* contain the specified index name index value.
* Find a Map of the session id to the {@link Session} of all sessions that contain
* the session attribute with the name
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the value of
* the specified principal name.
*
* @param indexName the name of the index (i.e.
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
* @param indexValue the value of the index to search for.
* @return a {@code Map} (never {@code null}) of the session id to the {@code Session}
* of all sessions that contain the specified index name and index value. If no
* results are found, an empty {@code Map} is returned.
* @return a Map (never null) of the session id to the {@link Session} of all sessions
* that contain the session specified index name and the value of the specified index
* name. If no results are found, an empty Map is returned.
*/
Map<String, S> findByIndexNameAndIndexValue(String indexName, String indexValue);
/**
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
* contain the index with the name
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the
* specified principal name.
*
* @param principalName the principal name
* @return a {@code Map} (never {@code null}) of the session id to the {@code Session}
* of all sessions that contain the specified principal name. If no results are found,
* an empty {@code Map} is returned.
* @since 2.1.0
*/
default Map<String, S> findByPrincipalName(String principalName) {
return findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);
}
}

View File

@@ -53,7 +53,7 @@ public final class MapSession implements Session, Serializable {
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
private String id;
private final String originalId;
private String originalId;
private Map<String, Object> sessionAttrs = new HashMap<>();
private Instant creationTime = Instant.now();
private Instant lastAccessedTime = this.creationTime;
@@ -132,6 +132,10 @@ public final class MapSession implements Session, Serializable {
return this.originalId;
}
void setOriginalId(String originalId) {
this.originalId = originalId;
}
@Override
public String changeSessionId() {
String changedId = generateId();

View File

@@ -73,6 +73,7 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
public void save(MapSession session) {
if (!session.getId().equals(session.getOriginalId())) {
this.sessions.remove(session.getOriginalId());
session.setOriginalId(session.getId());
}
this.sessions.put(session.getId(), new MapSession(session));
}

View File

@@ -76,6 +76,7 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
return Mono.fromRunnable(() -> {
if (!session.getId().equals(session.getOriginalId())) {
this.sessions.remove(session.getOriginalId());
session.setOriginalId(session.getId());
}
this.sessions.put(session.getId(), new MapSession(session));
});

View File

@@ -81,7 +81,7 @@ public interface Session {
@SuppressWarnings("unchecked")
default <T> T getAttributeOrDefault(String name, T defaultValue) {
T result = getAttribute(name);
return (result != null) ? result : defaultValue;
return (result != null ? result : defaultValue);
}
/**

View File

@@ -110,9 +110,8 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
@PostConstruct
public void init() {
CookieSerializer cookieSerializer = (this.cookieSerializer != null)
? this.cookieSerializer
: createDefaultCookieSerializer();
CookieSerializer cookieSerializer = (this.cookieSerializer != null
? this.cookieSerializer : createDefaultCookieSerializer());
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,8 +65,9 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
@Override
public List<SessionInformation> getAllSessions(Object principal,
boolean includeExpiredSessions) {
Collection<S> sessions = this.sessionRepository
.findByPrincipalName(name(principal)).values();
Collection<S> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
name(principal)).values();
List<SessionInformation> infos = new ArrayList<>();
for (S session : sessions) {
if (includeExpiredSessions || !Boolean.TRUE.equals(session

View File

@@ -16,13 +16,8 @@
package org.springframework.session.web.http;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.BitSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -46,22 +41,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
private static final Log logger = LogFactory.getLog(DefaultCookieSerializer.class);
private static final BitSet domainValid = new BitSet(128);
static {
for (char c = '0'; c <= '9'; c++) {
domainValid.set(c);
}
for (char c = 'a'; c <= 'z'; c++) {
domainValid.set(c);
}
for (char c = 'A'; c <= 'Z'; c++) {
domainValid.set(c);
}
domainValid.set('.');
domainValid.set('-');
}
private String cookieName = "SESSION";
private Boolean useSecureCookie;
@@ -82,8 +61,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
private String rememberMeRequestAttribute;
private String sameSite = "Lax";
/*
* (non-Javadoc)
*
@@ -98,8 +75,7 @@ public class DefaultCookieSerializer implements CookieSerializer {
for (Cookie cookie : cookies) {
if (this.cookieName.equals(cookie.getName())) {
String sessionId = (this.useBase64Encoding
? base64Decode(cookie.getValue())
: cookie.getValue());
? base64Decode(cookie.getValue()) : cookie.getValue());
if (sessionId == null) {
continue;
}
@@ -125,43 +101,37 @@ public class DefaultCookieSerializer implements CookieSerializer {
HttpServletRequest request = cookieValue.getRequest();
HttpServletResponse response = cookieValue.getResponse();
StringBuilder sb = new StringBuilder();
sb.append(this.cookieName).append('=');
String value = getValue(cookieValue);
if (value != null && value.length() > 0) {
validateValue(value);
sb.append(value);
}
int maxAge = getMaxAge(cookieValue);
if (maxAge > -1) {
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
OffsetDateTime expires = (maxAge != 0)
? OffsetDateTime.now().plusSeconds(maxAge)
: Instant.EPOCH.atOffset(ZoneOffset.UTC);
sb.append("; Expires=")
.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
}
String domain = getDomainName(request);
if (domain != null && domain.length() > 0) {
validateDomain(domain);
sb.append("; Domain=").append(domain);
}
String path = getCookiePath(request);
if (path != null && path.length() > 0) {
validatePath(path);
sb.append("; Path=").append(path);
}
if (isSecureCookie(request)) {
sb.append("; Secure");
}
if (this.useHttpOnlyCookie) {
sb.append("; HttpOnly");
}
if (this.sameSite != null) {
sb.append("; SameSite=").append(this.sameSite);
String requestedCookieValue = cookieValue.getCookieValue();
String actualCookieValue = (this.jvmRoute != null
? requestedCookieValue + this.jvmRoute : requestedCookieValue);
Cookie sessionCookie = new Cookie(this.cookieName, this.useBase64Encoding
? base64Encode(actualCookieValue) : actualCookieValue);
sessionCookie.setSecure(isSecureCookie(request));
sessionCookie.setPath(getCookiePath(request));
String domainName = getDomainName(request);
if (domainName != null) {
sessionCookie.setDomain(domainName);
}
response.addHeader("Set-Cookie", sb.toString());
if (this.useHttpOnlyCookie) {
sessionCookie.setHttpOnly(true);
}
if (cookieValue.getCookieMaxAge() < 0) {
if (this.rememberMeRequestAttribute != null
&& request.getAttribute(this.rememberMeRequestAttribute) != null) {
// the cookie is only written at time of session creation, so we rely on
// session expiration rather than cookie expiration if remember me is enabled
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
}
else if (this.cookieMaxAge != null) {
cookieValue.setCookieMaxAge(this.cookieMaxAge);
}
}
sessionCookie.setMaxAge(cookieValue.getCookieMaxAge());
response.addCookie(sessionCookie);
}
/**
@@ -192,81 +162,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
return new String(encodedCookieBytes);
}
private String getValue(CookieValue cookieValue) {
String requestedCookieValue = cookieValue.getCookieValue();
String actualCookieValue = requestedCookieValue;
if (this.jvmRoute != null) {
actualCookieValue = requestedCookieValue + this.jvmRoute;
}
if (this.useBase64Encoding) {
actualCookieValue = base64Encode(actualCookieValue);
}
return actualCookieValue;
}
private void validateValue(String value) {
int start = 0;
int end = value.length();
if ((end > 1) && (value.charAt(0) == '"') && (value.charAt(end - 1) == '"')) {
start = 1;
end--;
}
char[] chars = value.toCharArray();
for (int i = start; i < end; i++) {
char c = chars[i];
if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c
|| c == 0x7f) {
throw new IllegalArgumentException(
"Invalid character in cookie value: " + Integer.toString(c));
}
}
}
private int getMaxAge(CookieValue cookieValue) {
int maxAge = cookieValue.getCookieMaxAge();
if (maxAge < 0) {
if (this.rememberMeRequestAttribute != null && cookieValue.getRequest()
.getAttribute(this.rememberMeRequestAttribute) != null) {
// the cookie is only written at time of session creation, so we rely on
// session expiration rather than cookie expiration if remember me is
// enabled
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
}
else if (this.cookieMaxAge != null) {
cookieValue.setCookieMaxAge(this.cookieMaxAge);
}
}
return cookieValue.getCookieMaxAge();
}
private void validateDomain(String domain) {
int i = 0;
int cur = -1;
int prev;
char[] chars = domain.toCharArray();
while (i < chars.length) {
prev = cur;
cur = chars[i];
if (!domainValid.get(cur)
|| ((prev == '.' || prev == -1) && (cur == '.' || cur == '-'))
|| (prev == '-' && cur == '.')) {
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
}
i++;
}
if (cur == '.' || cur == '-') {
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
}
}
private void validatePath(String path) {
for (char ch : path.toCharArray()) {
if (ch < 0x20 || ch > 0x7E || ch == ';') {
throw new IllegalArgumentException("Invalid cookie path: " + path);
}
}
}
/**
* Sets if a Cookie marked as secure should be used. The default is to use the value
* of {@link HttpServletRequest#isSecure()}.
@@ -422,16 +317,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
this.rememberMeRequestAttribute = rememberMeRequestAttribute;
}
/**
* Set the value for the {@code SameSite} cookie directive. The default value is
* {@code Lax}.
* @param sameSite the SameSite directive value
* @since 2.1.0
*/
public void setSameSite(String sameSite) {
this.sameSite = sameSite;
}
private String getDomainName(HttpServletRequest request) {
if (this.domainName != null) {
return this.domainName;

View File

@@ -98,8 +98,8 @@ public class HeaderHttpSessionIdResolver implements HttpSessionIdResolver {
@Override
public List<String> resolveSessionIds(HttpServletRequest request) {
String headerValue = request.getHeader(this.headerName);
return (headerValue != null) ? Collections.singletonList(headerValue)
: Collections.emptyList();
return (headerValue != null ? Collections.singletonList(headerValue)
: Collections.emptyList());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,13 +24,8 @@ import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.session.Session;
/**
@@ -38,14 +33,11 @@ import org.springframework.session.Session;
*
* @param <S> the {@link Session} type
* @author Rob Winch
* @author Vedran Pavic
* @since 1.1
*/
@SuppressWarnings("deprecation")
class HttpSessionAdapter<S extends Session> implements HttpSession {
private static final Log logger = LogFactory.getLog(HttpSessionAdapter.class);
private S session;
private final ServletContext servletContext;
@@ -137,28 +129,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
@Override
public void setAttribute(String name, Object value) {
checkState();
Object oldValue = this.session.getAttribute(name);
this.session.setAttribute(name, value);
if (value != oldValue) {
if (oldValue instanceof HttpSessionBindingListener) {
try {
((HttpSessionBindingListener) oldValue).valueUnbound(
new HttpSessionBindingEvent(this, name, oldValue));
}
catch (Throwable th) {
logger.error("Error invoking session binding event listener", th);
}
}
if (value instanceof HttpSessionBindingListener) {
try {
((HttpSessionBindingListener) value)
.valueBound(new HttpSessionBindingEvent(this, name, value));
}
catch (Throwable th) {
logger.error("Error invoking session binding event listener", th);
}
}
}
}
@Override
@@ -169,17 +140,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
@Override
public void removeAttribute(String name) {
checkState();
Object oldValue = this.session.getAttribute(name);
this.session.removeAttribute(name);
if (oldValue instanceof HttpSessionBindingListener) {
try {
((HttpSessionBindingListener) oldValue)
.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
}
catch (Throwable th) {
logger.error("Error invoking session binding event listener", th);
}
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletResponse;
*
* @author Rob Winch
* @author Vedran Pavic
* @since 2.0.0
* @since 1.0
*/
public interface HttpSessionIdResolver {

View File

@@ -174,11 +174,11 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
private void trackContentLength(byte[] content) {
checkContentLength((content != null) ? content.length : 0);
checkContentLength(content != null ? content.length : 0);
}
private void trackContentLength(char[] content) {
checkContentLength((content != null) ? content.length : 0);
checkContentLength(content != null ? content.length : 0);
}
private void trackContentLength(int content) {
@@ -257,13 +257,13 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
@Override
public boolean equals(Object obj) {
return this.delegate.equals(obj);
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public int hashCode() {
return this.delegate.hashCode();
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
@Override
@@ -502,13 +502,13 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
@Override
public boolean equals(Object obj) {
return this.delegate.equals(obj);
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public int hashCode() {
return this.delegate.hashCode();
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
@Override

View File

@@ -205,8 +205,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
private boolean requestedSessionCached;
private String requestedSessionId;
private Boolean requestedSessionIdValid;
private boolean requestedSessionInvalidated;
@@ -279,6 +277,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
}
return isRequestedSessionIdValid(requestedSession);
}
return this.requestedSessionIdValid;
}
@@ -352,10 +351,8 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
@Override
public String getRequestedSessionId() {
if (this.requestedSessionId == null) {
getRequestedSession();
}
return this.requestedSessionId;
S requestedSession = getRequestedSession();
return (requestedSession != null ? requestedSession.getId() : null);
}
private S getRequestedSession() {
@@ -363,14 +360,10 @@ 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;
}
}
@@ -382,7 +375,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
private void clearRequestedSessionCache() {
this.requestedSessionCached = false;
this.requestedSession = null;
this.requestedSessionId = null;
}
/**

View File

@@ -87,6 +87,12 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
return Mono.just(session);
}
public Mono<Void> storeSession(WebSession session) {
@SuppressWarnings("unchecked")
SpringSessionWebSession springWebSession = (SpringSessionWebSession) session;
return this.sessions.save(springWebSession.session);
}
@Override
public Mono<WebSession> retrieveSession(String sessionId) {
return this.sessions.findById(sessionId)

View File

@@ -71,9 +71,9 @@ public final class WebSocketRegistryListener
SessionDisconnectEvent e = (SessionDisconnectEvent) event;
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor
.getSessionAttributes(e.getMessage().getHeaders());
String httpSessionId = (sessionAttributes != null)
String httpSessionId = (sessionAttributes != null
? SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes)
: null;
: null);
afterConnectionClosed(httpSessionId, e.getSessionId());
}
}

View File

@@ -117,9 +117,8 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
}
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor
.getSessionAttributes(message.getHeaders());
String sessionId = (sessionHeaders != null)
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME)
: null;
String sessionId = (sessionHeaders != null
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME) : null);
if (sessionId != null) {
S session = this.sessionRepository.findById(sessionId);
if (session != null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
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;
@@ -75,9 +74,7 @@ public class SpringSessionBackedSessionRegistryTest {
.getSessionInformation(SESSION_ID);
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
assertThat(
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
assertThat(sessionInfo.isExpired()).isFalse();
}
@@ -93,9 +90,7 @@ public class SpringSessionBackedSessionRegistryTest {
.getSessionInformation(SESSION_ID);
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
assertThat(
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
assertThat(sessionInfo.isExpired()).isTrue();
}
@@ -167,8 +162,9 @@ public class SpringSessionBackedSessionRegistryTest {
Map<String, Session> sessions = new LinkedHashMap<>();
sessions.put(session1.getId(), session1);
sessions.put(session2.getId(), session2);
when(this.sessionRepository.findByPrincipalName(USER_NAME))
.thenReturn(sessions);
when(this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, USER_NAME))
.thenReturn(sessions);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -26,7 +26,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.mock.web.MockCookie;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.session.web.http.CookieSerializer.CookieValue;
@@ -467,39 +466,6 @@ public class DefaultCookieSerializerTests {
assertThat(getCookie().getMaxAge()).isEqualTo(100);
}
// --- sameSite ---
@Test
public void writeCookieDefaultSameSiteLax() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
}
@Test
public void writeCookieSetSameSiteLax() {
this.serializer.setSameSite("Lax");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
}
@Test
public void writeCookieSetSameSiteStrict() {
this.serializer.setSameSite("Strict");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Strict");
}
@Test
public void writeCookieSetSameSiteNull() {
this.serializer.setSameSite(null);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isNull();
}
public void setCookieName(String cookieName) {
this.cookieName = cookieName;
this.serializer.setCookieName(cookieName);
@@ -512,8 +478,8 @@ public class DefaultCookieSerializerTests {
return new Cookie(name, value);
}
private MockCookie getCookie() {
return (MockCookie) this.response.getCookie(this.cookieName);
private Cookie getCookie() {
return this.response.getCookie(this.cookieName);
}
private String getCookieValue() {

View File

@@ -27,8 +27,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
@@ -38,8 +36,6 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import org.assertj.core.data.Offset;
@@ -1196,29 +1192,6 @@ 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);
@@ -1413,122 +1386,6 @@ public class SessionRepositoryFilterTests {
.hasMessage("httpSessionIdResolver cannot be null");
}
@Test
public void bindingListenerBindListener() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
session.setAttribute(bindingListenerName, bindingListener);
}
});
assertThat(bindingListener.getCounter()).isEqualTo(1);
}
@Test
public void bindingListenerBindListenerThenUnbind() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
session.setAttribute(bindingListenerName, bindingListener);
session.removeAttribute(bindingListenerName);
}
});
assertThat(bindingListener.getCounter()).isEqualTo(0);
}
@Test
public void bindingListenerBindSameListenerTwice() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
session.setAttribute(bindingListenerName, bindingListener);
session.setAttribute(bindingListenerName, bindingListener);
}
});
assertThat(bindingListener.getCounter()).isEqualTo(1);
}
@Test
public void bindingListenerBindListenerOverwrite() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener1 = new CountingHttpSessionBindingListener();
CountingHttpSessionBindingListener bindingListener2 = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
session.setAttribute(bindingListenerName, bindingListener1);
session.setAttribute(bindingListenerName, bindingListener2);
}
});
assertThat(bindingListener1.getCounter()).isEqualTo(0);
assertThat(bindingListener2.getCounter()).isEqualTo(1);
}
@Test
public void bindingListenerBindThrowsException() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
bindingListener.setThrowException();
session.setAttribute(bindingListenerName, bindingListener);
}
});
assertThat(bindingListener.getCounter()).isEqualTo(0);
}
@Test
public void bindingListenerBindListenerThenUnbindThrowsException() throws Exception {
String bindingListenerName = "bindingListener";
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
doFilter(new DoInFilter() {
@Override
public void doFilter(HttpServletRequest wrappedRequest) {
HttpSession session = wrappedRequest.getSession();
session.setAttribute(bindingListenerName, bindingListener);
bindingListener.setThrowException();
session.removeAttribute(bindingListenerName);
}
});
assertThat(bindingListener.getCounter()).isEqualTo(1);
}
// --- helper methods
private void assertNewSession() {
@@ -1631,39 +1488,4 @@ public class SessionRepositoryFilterTests {
}
private static class CountingHttpSessionBindingListener
implements HttpSessionBindingListener {
private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicBoolean throwException = new AtomicBoolean(false);
@Override
public void valueBound(HttpSessionBindingEvent event) {
if (this.throwException.get()) {
this.throwException.compareAndSet(true, false);
throw new RuntimeException("bind exception");
}
this.counter.incrementAndGet();
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
if (this.throwException.get()) {
this.throwException.compareAndSet(true, false);
throw new RuntimeException("unbind exception");
}
this.counter.decrementAndGet();
}
int getCounter() {
return this.counter.get();
}
void setThrowException() {
this.throwException.compareAndSet(false, true);
}
}
}

View File

@@ -253,6 +253,17 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
.containsExactly(new AbstractMap.SimpleEntry<>(attrName, attrValue));
}
@Test
public void storeSessionWhenInvokedThenSessionSaved() {
given(this.sessionRepository.save(this.createSession)).willReturn(Mono.empty());
WebSession createdSession = this.webSessionStore.createWebSession()
.block();
this.webSessionStore.storeSession(createdSession).block();
verify(this.sessionRepository).save(this.createSession);
}
@Test
public void retrieveSessionThenStarted() {
String id = "id";

View File

@@ -29,7 +29,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
*/
public abstract class AbstractRedisITests {
private static final String DOCKER_IMAGE = "redis:4.0.11";
private static final String DOCKER_IMAGE = "redis:4.0.10";
protected static class BaseConfig {

View File

@@ -16,8 +16,6 @@
package org.springframework.session.data.redis;
import java.time.Instant;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,7 +28,6 @@ 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}.
@@ -194,31 +191,6 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
assertThat(this.repository.findById(originalId).block()).isNull();
}
// gh-1111
@Test
public void changeSessionSaveOldSessionInstance() {
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
.createSession().block();
String sessionId = toSave.getId();
this.repository.save(toSave).block();
ReactiveRedisOperationsSessionRepository.RedisSession session = this.repository
.findById(sessionId).block();
session.changeSessionId();
session.setLastAccessedTime(Instant.now());
this.repository.save(session).block();
toSave.setLastAccessedTime(Instant.now());
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();
}
@Configuration
@EnableRedisWebSession
static class Config extends BaseConfig {

View File

@@ -16,7 +16,6 @@
package org.springframework.session.data.redis;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
@@ -191,10 +190,9 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
+ toSave.getId();
String channel = "__keyevent@0__:expired";
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
String channel = ":expired";
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
body.getBytes("UTF-8"));
byte[] pattern = new byte[] {};
this.repository.onMessage(message, pattern);
@@ -360,10 +358,9 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
+ toSave.getId();
String channel = "__keyevent@0__:expired";
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
String channel = ":expired";
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
body.getBytes("UTF-8"));
byte[] pattern = new byte[] {};
this.repository.onMessage(message, pattern);
@@ -584,22 +581,6 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
assertThat(this.repository.findById(originalId)).isNull();
}
// gh-1137
@Test
public void changeSessionIdWhenSessionIsDeleted() {
RedisSession toSave = this.repository.createSession();
String sessionId = toSave.getId();
this.repository.save(toSave);
this.repository.deleteById(sessionId);
toSave.changeSessionId();
this.repository.save(toSave);
assertThat(this.repository.findById(toSave.getId())).isNull();
assertThat(this.repository.findById(sessionId)).isNull();
}
private String getSecurityName() {
return this.context.getAuthentication().getName();
}

View File

@@ -95,7 +95,7 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
synchronized (this.lock) {
this.lock.wait(TimeUnit.SECONDS.toMillis(1));
}
return (this.taskDispatched != null) ? this.taskDispatched : Boolean.FALSE;
return (this.taskDispatched != null ? this.taskDispatched : Boolean.FALSE);
}
}

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -118,15 +118,6 @@ public class ReactiveRedisOperationsSessionRepository implements
this.redisFlushMode = redisFlushMode;
}
/**
* Returns the {@link ReactiveRedisOperations} used for sessions.
* @return the {@link ReactiveRedisOperations} used for sessions
* @since 2.1.0
*/
public ReactiveRedisOperations<String, Object> getSessionRedisOperations() {
return this.sessionRedisOperations;
}
@Override
public Mono<RedisSession> createSession() {
return Mono.defer(() -> {
@@ -143,38 +134,24 @@ public class ReactiveRedisOperationsSessionRepository implements
@Override
public Mono<Void> save(RedisSession session) {
Mono<Void> result = session.saveChangeSessionId().and(session.saveDelta())
.and((s) -> {
session.isNew = false;
s.onComplete();
});
if (session.isNew) {
return result;
}
else {
String sessionKey = getSessionKey(
session.hasChangedSessionId() ? session.originalSessionId
: session.getId());
return this.sessionRedisOperations.hasKey(sessionKey)
.flatMap((exists) -> exists ? result
: Mono.error(new IllegalStateException(
"Session was invalidated")));
}
return session.saveDelta().and((s) -> {
if (session.isNew) {
session.setNew(false);
}
s.onComplete();
});
}
@Override
public Mono<RedisSession> findById(String id) {
String sessionKey = getSessionKey(id);
// @formatter:off
return this.sessionRedisOperations.opsForHash().entries(sessionKey)
.collectMap((e) -> e.getKey().toString(), Map.Entry::getValue)
.filter((map) -> !map.isEmpty())
.map(new SessionMapper(id))
.filter((session) -> !session.isExpired())
.map(RedisSession::new)
.filter((map) -> !map.isEmpty()).map(new SessionMapper(id))
.filter((session) -> !session.isExpired()).map(RedisSession::new)
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
// @formatter:on
}
@Override
@@ -299,8 +276,12 @@ public class ReactiveRedisOperationsSessionRepository implements
return this.cached.isExpired();
}
private boolean hasChangedSessionId() {
return !getId().equals(this.originalSessionId);
public void setNew(boolean isNew) {
this.isNew = isNew;
}
public boolean isNew() {
return this.isNew;
}
private void flushImmediateIfNecessary() {
@@ -315,35 +296,38 @@ public class ReactiveRedisOperationsSessionRepository implements
}
private Mono<Void> saveDelta() {
String sessionId = getId();
Mono<Void> changeSessionId = saveChangeSessionId(sessionId);
if (this.delta.isEmpty()) {
return Mono.empty();
return changeSessionId.and(Mono.empty());
}
String sessionKey = getSessionKey(getId());
String sessionKey = getSessionKey(sessionId);
Mono<Boolean> update = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
.opsForHash().putAll(sessionKey, this.delta);
Mono<Boolean> setTtl = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
.expire(sessionKey, getMaxInactiveInterval());
return update.and(setTtl).and((s) -> {
return changeSessionId.and(update).and(setTtl).and((s) -> {
this.delta.clear();
s.onComplete();
}).then();
}
private Mono<Void> saveChangeSessionId() {
if (!hasChangedSessionId()) {
private Mono<Void> saveChangeSessionId(String sessionId) {
if (sessionId.equals(this.originalSessionId)) {
return Mono.empty();
}
String sessionId = getId();
Publisher<Void> replaceSessionId = (s) -> {
this.originalSessionId = sessionId;
s.onComplete();
};
if (this.isNew) {
if (isNew()) {
return Mono.from(replaceSessionId);
}
else {

View File

@@ -28,8 +28,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.BoundHashOperations;
@@ -254,11 +252,6 @@ 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.
*/
@@ -291,19 +284,11 @@ 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;
@@ -340,7 +325,6 @@ public class RedisOperationsSessionRepository implements
this.sessionRedisOperations = sessionRedisOperations;
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations,
this::getExpirationsKey, this::getSessionKey);
configureSessionChannels();
}
/**
@@ -391,27 +375,6 @@ 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
* @since 2.0.0
*/
public RedisOperations<Object, Object> getSessionRedisOperations() {
return this.sessionRedisOperations;
}
@@ -532,7 +495,7 @@ public class RedisOperationsSessionRepository implements
String channel = new String(messageChannel);
if (channel.startsWith(this.sessionCreatedChannelPrefix)) {
if (channel.startsWith(getSessionCreatedChannelPrefix())) {
// TODO: is this thread safe?
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer
.deserialize(message.getBody());
@@ -545,8 +508,8 @@ public class RedisOperationsSessionRepository implements
return;
}
boolean isDeleted = channel.equals(this.sessionDeletedChannel);
if (isDeleted || channel.equals(this.sessionExpiredChannel)) {
boolean isDeleted = channel.endsWith(":del");
if (isDeleted || channel.endsWith(":expired")) {
int beginIndex = body.lastIndexOf(":") + 1;
int endIndex = body.length();
String sessionId = body.substring(beginIndex, endIndex);
@@ -609,7 +572,6 @@ public class RedisOperationsSessionRepository implements
public void setRedisKeyNamespace(String namespace) {
Assert.hasText(namespace, "namespace cannot be null or empty");
this.namespace = namespace.trim() + ":";
configureSessionChannels();
}
/**
@@ -641,33 +603,17 @@ public class RedisOperationsSessionRepository implements
}
private String getExpiredKeyPrefix() {
return this.namespace + "sessions:expires:";
return this.namespace + "sessions:" + "expires:";
}
/**
* 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
* 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
*/
public String getSessionCreatedChannelPrefix() {
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;
return this.namespace + "event:created:";
}
/**
@@ -851,10 +797,9 @@ public class RedisOperationsSessionRepository implements
this.delta = new HashMap<>(this.delta.size());
Long originalExpiration = (this.originalLastAccessTime != null)
? this.originalLastAccessTime.plus(getMaxInactiveInterval())
.toEpochMilli()
: null;
Long originalExpiration = (this.originalLastAccessTime != null
? this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli()
: null);
RedisOperationsSessionRepository.this.expirationPolicy
.onExpirationUpdated(originalExpiration, this);
}
@@ -868,16 +813,8 @@ public class RedisOperationsSessionRepository implements
originalSessionIdKey, sessionIdKey);
String originalExpiredKey = getExpiredKey(this.originalSessionId);
String expiredKey = getExpiredKey(sessionId);
try {
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(
originalExpiredKey, expiredKey);
}
catch (NonTransientDataAccessException ex) {
if (!"ERR no such key".equals(NestedExceptionUtils
.getMostSpecificCause(ex).getMessage())) {
throw ex;
}
}
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(
originalExpiredKey, expiredKey);
}
this.originalSessionId = sessionId;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,14 +23,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
/**
* Annotation used to inject the Redis accessor used by Spring Session's Redis session
* repository.
* Annotation used to inject the {@link RedisOperations} instance used by Spring Session's
* {@link RedisOperationsSessionRepository}.
*
* @author Vedran Pavic
* @see org.springframework.session.data.redis.RedisOperationsSessionRepository#getSessionRedisOperations()
* @see org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository#getSessionRedisOperations()
* @since 2.0.0
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,

View File

@@ -37,10 +37,7 @@ 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;
@@ -57,7 +54,6 @@ 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;
@@ -119,8 +115,6 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
}
sessionRepository.setRedisFlushMode(this.redisFlushMode);
int database = resolveDatabase();
sessionRepository.setDatabase(database);
return sessionRepository;
}
@@ -134,9 +128,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
if (this.redisSubscriptionExecutor != null) {
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
}
container.addMessageListener(sessionRepository(), Arrays.asList(
new ChannelTopic(sessionRepository().getSessionDeletedChannel()),
new ChannelTopic(sessionRepository().getSessionExpiredChannel())));
container.addMessageListener(sessionRepository(),
Arrays.asList(new PatternTopic("__keyevent@*:del"),
new PatternTopic("__keyevent@*:expired")));
container.addMessageListener(sessionRepository(),
Collections.singletonList(new PatternTopic(
sessionRepository().getSessionCreatedChannelPrefix() + "*")));
@@ -262,18 +256,6 @@ 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.

View File

@@ -21,7 +21,6 @@ import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -65,8 +64,6 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
private ReactiveRedisConnectionFactory redisConnectionFactory;
private RedisSerializer<Object> defaultRedisSerializer;
private ClassLoader classLoader;
private StringValueResolver embeddedValueResolver;
@@ -110,13 +107,6 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
this.redisConnectionFactory = redisConnectionFactoryToUse;
}
@Autowired(required = false)
@Qualifier("springSessionDefaultRedisSerializer")
public void setDefaultRedisSerializer(
RedisSerializer<Object> defaultRedisSerializer) {
this.defaultRedisSerializer = defaultRedisSerializer;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
@@ -144,11 +134,10 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
RedisSerializer<String> keySerializer = new StringRedisSerializer();
RedisSerializer<Object> defaultSerializer = (this.defaultRedisSerializer != null)
? this.defaultRedisSerializer
: new JdkSerializationRedisSerializer(this.classLoader);
RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(
this.classLoader);
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
.<String, Object>newSerializationContext(defaultSerializer)
.<String, Object>newSerializationContext(valueSerializer)
.key(keySerializer).hashKey(keySerializer).build();
return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
serializationContext);

View File

@@ -183,7 +183,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
@Test
public void saveSessionNothingChanged() {
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
given(this.redisOperations.expire(anyString(), any()))
.willReturn(Mono.just(true));
@@ -192,14 +191,12 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
StepVerifier.create(this.repository.save(session)).verifyComplete();
verify(this.redisOperations).hasKey(anyString());
verifyZeroInteractions(this.redisOperations);
verifyZeroInteractions(this.hashOperations);
}
@Test
public void saveLastAccessChanged() {
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
given(this.redisOperations.expire(anyString(), any()))
@@ -209,7 +206,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
Mono.just(session).subscribe(this.repository::save);
verify(this.redisOperations).hasKey(anyString());
verify(this.redisOperations).opsForHash();
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
verify(this.redisOperations).expire(anyString(), any());
@@ -223,7 +219,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
@Test
public void saveSetAttribute() {
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
given(this.redisOperations.expire(anyString(), any()))
@@ -234,7 +229,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
session.setAttribute(attrName, "attrValue");
Mono.just(session).subscribe(this.repository::save);
verify(this.redisOperations).hasKey(anyString());
verify(this.redisOperations).opsForHash();
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
verify(this.redisOperations).expire(anyString(), any());
@@ -248,7 +242,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
@Test
public void saveRemoveAttribute() {
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
given(this.redisOperations.expire(anyString(), any()))
@@ -259,7 +252,6 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
session.removeAttribute(attrName);
Mono.just(session).subscribe(this.repository::save);
verify(this.redisOperations).hasKey(anyString());
verify(this.redisOperations).opsForHash();
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
verify(this.redisOperations).expire(anyString(), any());
@@ -346,16 +338,12 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
.isEqualTo(expected.getAttribute(attribute1));
assertThat(session.<String>getAttribute(attribute2))
.isEqualTo(expected.getAttribute(attribute2));
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(expected.getCreationTime()
.truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getMaxInactiveInterval())
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
assertThat(session.getMaxInactiveInterval())
.isEqualTo(expected.getMaxInactiveInterval());
assertThat(
session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(expected.getLastAccessedTime()
.truncatedTo(ChronoUnit.MILLIS));
}).verifyComplete();
assertThat(session.getLastAccessedTime())
.isEqualTo(expected.getLastAccessedTime());
}).verifyComplete();
}
@Test

View File

@@ -16,7 +16,6 @@
package org.springframework.session.data.redis;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -432,12 +431,12 @@ public class RedisOperationsSessionRepositoryTests {
.isEqualTo(expected.getAttribute(attribute1));
assertThat(session.<String>getAttribute(attribute2))
.isEqualTo(expected.getAttribute(attribute2));
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(expected.getCreationTime().truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
assertThat(session.getMaxInactiveInterval())
.isEqualTo(expected.getMaxInactiveInterval());
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(expected.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getLastAccessedTime())
.isEqualTo(expected.getLastAccessedTime());
}
@Test
@@ -498,11 +497,9 @@ public class RedisOperationsSessionRepositoryTests {
RedisSession session = sessionIdToSessions.get(sessionId);
assertThat(session).isNotNull();
assertThat(session.getId()).isEqualTo(sessionId);
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(lastAccessed.truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessed);
assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactive);
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(createdTime.truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getCreationTime()).isEqualTo(createdTime);
}
@Test
@@ -525,15 +522,14 @@ public class RedisOperationsSessionRepositoryTests {
}
@Test
public void onMessageCreated() {
public void onMessageCreated() throws Exception {
MapSession session = this.cached;
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
String channel = "spring:session:event:0:created:" + session.getId();
byte[] pattern = "".getBytes("UTF-8");
String channel = "spring:session:event:created:" + session.getId();
JdkSerializationRedisSerializer defaultSerailizer = new JdkSerializationRedisSerializer();
this.redisRepository.setDefaultSerializer(defaultSerailizer);
byte[] body = defaultSerailizer.serialize(new HashMap());
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8), body);
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
this.redisRepository.setApplicationEventPublisher(this.publisher);
@@ -543,16 +539,16 @@ public class RedisOperationsSessionRepositoryTests {
assertThat(this.event.getValue().getSessionId()).isEqualTo(session.getId());
}
@Test // gh-309
public void onMessageCreatedCustomSerializer() {
// gh-309
@Test
public void onMessageCreatedCustomSerializer() throws Exception {
MapSession session = this.cached;
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
byte[] pattern = "".getBytes("UTF-8");
byte[] body = new byte[0];
String channel = "spring:session:event:0:created:" + session.getId();
String channel = "spring:session:event:created:" + session.getId();
given(this.defaultSerializer.deserialize(body))
.willReturn(new HashMap<String, Object>());
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8), body);
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, pattern);
@@ -563,7 +559,7 @@ public class RedisOperationsSessionRepositoryTests {
}
@Test
public void onMessageDeletedSessionFound() {
public void onMessageDeletedSessionFound() throws Exception {
String deletedId = "deleted-id";
given(this.redisOperations.boundHashOps(getKey(deletedId)))
.willReturn(this.boundHashOperations);
@@ -574,12 +570,10 @@ public class RedisOperationsSessionRepositoryTests {
String channel = "__keyevent@0__:del";
String body = "spring:session:sessions:expires:" + deletedId;
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
verify(this.boundHashOperations).entries();
@@ -592,7 +586,7 @@ public class RedisOperationsSessionRepositoryTests {
}
@Test
public void onMessageDeletedSessionNotFound() {
public void onMessageDeletedSessionNotFound() throws Exception {
String deletedId = "deleted-id";
given(this.redisOperations.boundHashOps(getKey(deletedId)))
.willReturn(this.boundHashOperations);
@@ -600,12 +594,10 @@ public class RedisOperationsSessionRepositoryTests {
String channel = "__keyevent@0__:del";
String body = "spring:session:sessions:expires:" + deletedId;
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
verify(this.boundHashOperations).entries();
@@ -616,7 +608,7 @@ public class RedisOperationsSessionRepositoryTests {
}
@Test
public void onMessageExpiredSessionFound() {
public void onMessageExpiredSessionFound() throws Exception {
String expiredId = "expired-id";
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
@@ -627,12 +619,10 @@ public class RedisOperationsSessionRepositoryTests {
String channel = "__keyevent@0__:expired";
String body = "spring:session:sessions:expires:" + expiredId;
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
verify(this.boundHashOperations).entries();
@@ -645,7 +635,7 @@ public class RedisOperationsSessionRepositoryTests {
}
@Test
public void onMessageExpiredSessionNotFound() {
public void onMessageExpiredSessionNotFound() throws Exception {
String expiredId = "expired-id";
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
@@ -653,12 +643,10 @@ public class RedisOperationsSessionRepositoryTests {
String channel = "__keyevent@0__:expired";
String body = "spring:session:sessions:expires:" + expiredId;
DefaultMessage message = new DefaultMessage(
channel.getBytes(StandardCharsets.UTF_8),
body.getBytes(StandardCharsets.UTF_8));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
verify(this.boundHashOperations).entries();
@@ -893,62 +881,6 @@ 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;
}

View File

@@ -27,12 +27,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
import org.springframework.session.data.redis.RedisFlushMode;
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -73,22 +70,6 @@ public class RedisWebSessionConfigurationTests {
assertThat(repository).isNotNull();
}
@Test
public void springSessionRedisOperationsResolvingConfiguration() {
registerAndRefresh(RedisConfig.class,
SpringSessionRedisOperationsResolvingConfig.class);
ReactiveRedisOperationsSessionRepository repository = this.context
.getBean(ReactiveRedisOperationsSessionRepository.class);
assertThat(repository).isNotNull();
ReactiveRedisOperations<String, Object> springSessionRedisOperations = this.context
.getBean(SpringSessionRedisOperationsResolvingConfig.class)
.getSpringSessionRedisOperations();
assertThat(springSessionRedisOperations).isNotNull();
assertThat((ReactiveRedisOperations) ReflectionTestUtils.getField(repository,
"sessionRedisOperations")).isEqualTo(springSessionRedisOperations);
}
@Test
public void customNamespace() {
registerAndRefresh(RedisConfig.class, CustomNamespaceConfig.class);
@@ -200,36 +181,6 @@ public class RedisWebSessionConfigurationTests {
.hasMessageContaining("expected single matching bean but found 2");
}
@Test
@SuppressWarnings("unchecked")
public void customRedisSerializerConfig() {
registerAndRefresh(RedisConfig.class, CustomRedisSerializerConfig.class);
ReactiveRedisOperationsSessionRepository repository = this.context
.getBean(ReactiveRedisOperationsSessionRepository.class);
RedisSerializer<Object> redisSerializer = this.context
.getBean("springSessionDefaultRedisSerializer", RedisSerializer.class);
assertThat(repository).isNotNull();
assertThat(redisSerializer).isNotNull();
ReactiveRedisOperations redisOperations = (ReactiveRedisOperations) ReflectionTestUtils
.getField(repository, "sessionRedisOperations");
assertThat(redisOperations).isNotNull();
RedisSerializationContext serializationContext = redisOperations
.getSerializationContext();
assertThat(ReflectionTestUtils.getField(
serializationContext.getValueSerializationPair().getReader(),
"serializer")).isEqualTo(redisSerializer);
assertThat(ReflectionTestUtils.getField(
serializationContext.getValueSerializationPair().getWriter(),
"serializer")).isEqualTo(redisSerializer);
assertThat(ReflectionTestUtils.getField(
serializationContext.getHashValueSerializationPair().getReader(),
"serializer")).isEqualTo(redisSerializer);
assertThat(ReflectionTestUtils.getField(
serializationContext.getHashValueSerializationPair().getWriter(),
"serializer")).isEqualTo(redisSerializer);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
@@ -250,18 +201,6 @@ public class RedisWebSessionConfigurationTests {
}
@EnableRedisWebSession
static class SpringSessionRedisOperationsResolvingConfig {
@SpringSessionRedisOperations
private ReactiveRedisOperations<String, Object> springSessionRedisOperations;
public ReactiveRedisOperations<String, Object> getSpringSessionRedisOperations() {
return this.springSessionRedisOperations;
}
}
@EnableRedisWebSession(redisNamespace = REDIS_NAMESPACE)
static class CustomNamespaceConfig {
@@ -336,15 +275,4 @@ public class RedisWebSessionConfigurationTests {
}
@EnableRedisWebSession
static class CustomRedisSerializerConfig {
@Bean
@SuppressWarnings("unchecked")
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return mock(RedisSerializer.class);
}
}
}

View File

@@ -3,7 +3,6 @@ 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"

View File

@@ -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.6")
"hazelcast/hazelcast:3.9.4")
.withExposedPorts(5701)
.withEnv("JAVA_OPTS",
"-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml")

View File

@@ -1 +0,0 @@
ryuk.container.timeout=120

View File

@@ -20,7 +20,6 @@ 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;
@@ -33,8 +32,7 @@ import org.springframework.session.MapSession;
* @since 2.0.5
* @see HazelcastSessionRepository#save(HazelcastSessionRepository.HazelcastSession)
*/
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession>
implements Offloadable {
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession> {
private Instant lastAccessedTime;
@@ -68,11 +66,6 @@ class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSess
return Boolean.TRUE;
}
@Override
public String getExecutorName() {
return OFFLOADABLE_EXECUTOR;
}
void setLastAccessedTime(Instant lastAccessedTime) {
this.lastAccessedTime = lastAccessedTime;
}

View File

@@ -13,7 +13,6 @@ 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"
@@ -22,6 +21,5 @@ dependencies {
integrationTestCompile "org.testcontainers:mariadb"
integrationTestCompile "org.testcontainers:mssqlserver"
integrationTestCompile "org.testcontainers:mysql"
integrationTestCompile "org.testcontainers:oracle-xe"
integrationTestCompile "org.testcontainers:postgresql"
}

View File

@@ -1,59 +0,0 @@
/*
* 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;
}
}
}

View File

@@ -21,7 +21,6 @@ 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;
@@ -39,7 +38,6 @@ 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;
@@ -49,6 +47,7 @@ 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 {
@@ -64,7 +63,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
private SecurityContext changedContext;
@Before
public void setUp() {
public void setup() throws Exception {
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
@@ -77,13 +76,12 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void saveWhenNoAttributesThenCanBeFound() {
public void saveWhenNoAttributesThenCanBeFound() throws Exception {
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();
@@ -91,7 +89,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void saves() {
public void saves() throws InterruptedException {
String username = "saves-" + System.currentTimeMillis();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
@@ -108,8 +106,7 @@ 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();
@@ -146,8 +143,7 @@ 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);
@@ -161,8 +157,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);
@@ -170,19 +166,17 @@ 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().truncatedTo(ChronoUnit.MILLIS))
.isEqualTo(lastAccessedTime.truncatedTo(ChronoUnit.MILLIS));
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessedTime);
}
@Test
public void findByPrincipalName() {
public void findByPrincipalName() throws Exception {
String principalName = "findByPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
@@ -206,14 +200,14 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByPrincipalNameExpireRemovesIndex() {
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
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();
@@ -226,7 +220,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByPrincipalNameNoPrincipalNameChange() {
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChange"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
@@ -250,7 +244,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByPrincipalNameNoPrincipalNameChangeReload() {
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
@@ -276,7 +270,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByDeletedPrincipalName() {
public void findByDeletedPrincipalName() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
@@ -294,7 +288,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByChangedPrincipalName() {
public void findByChangedPrincipalName() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
@@ -322,7 +316,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByDeletedPrincipalNameReload() {
public void findByDeletedPrincipalNameReload() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
@@ -342,7 +336,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByChangedPrincipalNameReload() {
public void findByChangedPrincipalNameReload() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
@@ -373,7 +367,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findBySecurityPrincipalName() {
public void findBySecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -396,12 +390,12 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findBySecurityPrincipalNameExpireRemovesIndex() {
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
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();
@@ -414,7 +408,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChange() {
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -436,7 +430,8 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload() {
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -460,7 +455,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByDeletedSecurityPrincipalName() {
public void findByDeletedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -477,7 +472,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByChangedSecurityPrincipalName() {
public void findByChangedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -503,7 +498,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByDeletedSecurityPrincipalNameReload() {
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -522,7 +517,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void findByChangedSecurityPrincipalNameReload() {
public void findByChangedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
@@ -609,17 +604,15 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void changeSessionIdWhenOnlyChangeId() {
public void changeSessionIdWhenOnlyChangeId() throws Exception {
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);
@@ -630,19 +623,16 @@ 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() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
public void changeSessionIdWhenChangeTwice() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
this.repository.save(toSave);
@@ -658,17 +648,15 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@Test
public void changeSessionIdWhenSetAttributeOnChangedSession() {
public void changeSessionIdWhenSetAttributeOnChangedSession() throws Exception {
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);
@@ -679,19 +667,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() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
public void changeSessionIdWhenHasNotSaved() throws Exception {
String attrName = "changeSessionId";
String attrValue = "changeSessionId-value";
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
String originalId = toSave.getId();
toSave.changeSessionId();
@@ -703,8 +691,7 @@ 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");
@@ -717,8 +704,7 @@ 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");
@@ -731,8 +717,7 @@ 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());
@@ -746,8 +731,7 @@ 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());
@@ -759,73 +743,6 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(session.<String>getAttribute("testName")).isEqualTo("testValue2");
}
@Test // gh-1031
public void saveDeleted() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
this.repository.save(session);
session = this.repository.findById(session.getId());
this.repository.deleteById(session.getId());
session.setLastAccessedTime(Instant.now());
this.repository.save(session);
assertThat(this.repository.findById(session.getId())).isNull();
}
@Test // gh-1031
public void saveDeletedAddAttribute() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
this.repository.save(session);
session = this.repository.findById(session.getId());
this.repository.deleteById(session.getId());
session.setLastAccessedTime(Instant.now());
session.setAttribute("testName", "testValue1");
this.repository.save(session);
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();
}
@@ -835,7 +752,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
@EnableJdbcHttpSession
static class BaseConfig {
protected static class BaseConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
@@ -843,5 +760,4 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
}
}
}

View File

@@ -1,172 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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"));
}
}

View File

@@ -16,11 +16,20 @@
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;
@@ -36,21 +45,55 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class MariaDb10JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static MariaDBContainer container = new MariaDb10Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public MariaDBContainer databaseContainer() {
MariaDBContainer databaseContainer = DatabaseContainers.mariaDb10();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() throws SQLException {
MariaDbDataSource dataSource = new MariaDbDataSource(container.getJdbcUrl());
dataSource.setUserName(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.mySql();
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.8");
}
@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci");
}
}

View File

@@ -16,11 +16,20 @@
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;
@@ -36,21 +45,56 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class MariaDb5JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static MariaDBContainer container = new MariaDb5Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public MariaDBContainer databaseContainer() {
MariaDBContainer databaseContainer = DatabaseContainers.mariaDb5();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() throws SQLException {
MariaDbDataSource dataSource = new MariaDbDataSource(container.getJdbcUrl());
dataSource.setUserName(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.mySql();
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.60");
}
@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");
}
}

View File

@@ -16,11 +16,19 @@
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;
@@ -35,21 +43,61 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class MySql5JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static MySQLContainer container = new MySql5Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public MySQLContainer databaseContainer() {
MySQLContainer databaseContainer = DatabaseContainers.mySql5();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(container.getJdbcUrl());
dataSource.setUser(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.mySql();
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.22");
}
@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();
}
}

View File

@@ -16,11 +16,19 @@
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;
@@ -35,21 +43,60 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class MySql8JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static MySQLContainer container = new MySql8Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public MySQLContainer databaseContainer() {
MySQLContainer databaseContainer = DatabaseContainers.mySql8();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(container.getJdbcUrl());
dataSource.setUser(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.mySql();
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.11");
}
@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--default-authentication-plugin=mysql_native_password");
}
@Override
public String getDriverClassName() {
return Driver.class.getName();
}
}

View File

@@ -1,73 +0,0 @@
/*
* 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();
}
}
}

View File

@@ -16,11 +16,18 @@
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;
@@ -36,21 +43,50 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class PostgreSql10JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static PostgreSQLContainer container = new PostgreSql10Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public PostgreSQLContainer databaseContainer() {
PostgreSQLContainer databaseContainer = DatabaseContainers.postgreSql10();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() {
PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setUrl(container.getJdbcUrl());
dataSource.setUser(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.postgreSql();
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.4");
}
}

View File

@@ -16,11 +16,18 @@
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;
@@ -36,21 +43,50 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class PostgreSql9JdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static PostgreSQLContainer container = new PostgreSql9Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public PostgreSQLContainer databaseContainer() {
PostgreSQLContainer databaseContainer = DatabaseContainers.postgreSql9();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() {
PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setUrl(container.getJdbcUrl());
dataSource.setUser(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.postgreSql();
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.9");
}
}

View File

@@ -16,11 +16,18 @@
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;
@@ -36,21 +43,52 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
@ContextConfiguration
public class SqlServerJdbcOperationsSessionRepositoryITests
extends AbstractContainerJdbcOperationsSessionRepositoryITests {
extends AbstractJdbcOperationsSessionRepositoryITests {
private static MSSQLServerContainer container = new SqlServer2007Container();
@BeforeClass
public static void setUpClass() {
container.start();
}
@AfterClass
public static void tearDownClass() {
container.stop();
}
@Configuration
static class Config extends BaseContainerConfig {
static class Config extends BaseConfig {
@Bean
public MSSQLServerContainer databaseContainer() {
MSSQLServerContainer databaseContainer = DatabaseContainers.sqlServer2017();
databaseContainer.start();
return databaseContainer;
public DataSource dataSource() {
SQLServerDataSource dataSource = new SQLServerDataSource();
dataSource.setURL(container.getJdbcUrl());
dataSource.setUser(container.getUsername());
dataSource.setPassword(container.getPassword());
return dataSource;
}
@Bean
public ResourceDatabasePopulator databasePopulator() {
return DatabasePopulators.sqlServer();
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-CU8");
withStartupTimeoutSeconds(240);
withConnectTimeoutSeconds(240);
}
}

Some files were not shown because too many files have changed in this diff Show More