Date: Thu, 10 Aug 2023 15:51:41 +0800
Subject: [PATCH 038/579] Use spring-boot's @ServiceConnection
---
...ession-sample-boot-mongodb-reactive.gradle | 11 ++---
.../mongodb/examples/MongoDbConfig.java | 37 ++++++++++++++++
...SpringSessionMongoReactiveApplication.java | 43 +------------------
.../mongodb/examples/AttributeTests.java | 6 +--
...ion-sample-boot-mongodb-traditional.gradle | 11 ++---
.../SpringSessionMongoTraditionalBoot.java | 43 +------------------
.../examples/config/MongoDbConfig.java | 37 ++++++++++++++++
.../session/mongodb/examples/BootTests.java | 9 +---
8 files changed, 93 insertions(+), 104 deletions(-)
create mode 100644 spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/MongoDbConfig.java
create mode 100644 spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/config/MongoDbConfig.java
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/spring-session-sample-boot-mongodb-reactive.gradle b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/spring-session-sample-boot-mongodb-reactive.gradle
index cfca69a5b..7cf2b4284 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/spring-session-sample-boot-mongodb-reactive.gradle
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/spring-session-sample-boot-mongodb-reactive.gradle
@@ -1,11 +1,12 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
- implementation project(':spring-session-data-mongodb')
- implementation "org.springframework.boot:spring-boot-starter-webflux"
- implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
- implementation "org.springframework.boot:spring-boot-starter-data-mongodb-reactive"
- implementation "org.testcontainers:mongodb"
+ implementation project(':spring-session-data-mongodb')
+ implementation "org.springframework.boot:spring-boot-starter-webflux"
+ implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
+ implementation "org.springframework.boot:spring-boot-starter-data-mongodb-reactive"
+ implementation "org.springframework.boot:spring-boot-testcontainers"
+ implementation "org.testcontainers:mongodb"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.seleniumhq.selenium:htmlunit-driver"
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/MongoDbConfig.java b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/MongoDbConfig.java
new file mode 100644
index 000000000..14bab6d17
--- /dev/null
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/MongoDbConfig.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.mongodb.examples;
+
+import org.testcontainers.containers.MongoDBContainer;
+
+import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Yanming Zhou
+ */
+@Configuration(proxyBeanMethods = false)
+public class MongoDbConfig {
+
+ @Bean
+ @ServiceConnection
+ MongoDBContainer mongoDbContainer() {
+ return new MongoDBContainer("mongo:5.0.11");
+ }
+
+}
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoReactiveApplication.java b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoReactiveApplication.java
index f1c55a26e..fb8fca91b 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoReactiveApplication.java
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoReactiveApplication.java
@@ -16,17 +16,8 @@
package org.springframework.session.mongodb.examples;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.testcontainers.containers.MongoDBContainer;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.MapPropertySource;
import org.springframework.session.data.mongo.config.annotation.web.reactive.EnableMongoWebSession;
/**
@@ -35,44 +26,14 @@
*
* @author Rob Winch
* @author Greg Turnquist
+ * @author Yanming Zhou
*/
@SpringBootApplication
@EnableMongoWebSession
public class SpringSessionMongoReactiveApplication {
public static void main(String[] args) {
- SpringApplication application = new SpringApplication(SpringSessionMongoReactiveApplication.class);
- application.addInitializers(new Initializer());
- application.run(args);
- }
-
- /**
- * Use Testcontainers to managed MongoDB through Docker.
- *
- *
- * @see Local
- * Development with Testcontainers
- */
- static class Initializer implements ApplicationContextInitializer {
-
- static MongoDBContainer mongo = new MongoDBContainer("mongo:5.0.11");
-
- private static Map getProperties() {
- mongo.start();
-
- HashMap properties = new HashMap<>();
- properties.put("spring.data.mongodb.host", mongo.getHost());
- properties.put("spring.data.mongodb.port", mongo.getFirstMappedPort() + "");
- return properties;
- }
-
- @Override
- public void initialize(ConfigurableApplicationContext context) {
- ConfigurableEnvironment env = context.getEnvironment();
- env.getPropertySources().addFirst(new MapPropertySource("testcontainers", (Map) getProperties()));
- }
-
+ SpringApplication.run(SpringSessionMongoReactiveApplication.class, args);
}
}
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/test/java/org/springframework/session/mongodb/examples/AttributeTests.java b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/test/java/org/springframework/session/mongodb/examples/AttributeTests.java
index 00468e158..b5aed7dcc 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/test/java/org/springframework/session/mongodb/examples/AttributeTests.java
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-reactive/src/test/java/org/springframework/session/mongodb/examples/AttributeTests.java
@@ -21,7 +21,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
@@ -29,18 +28,15 @@
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.session.mongodb.examples.pages.HomePage;
import org.springframework.session.mongodb.examples.pages.HomePage.Attribute;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
+ * @author Yanming Zhou
*/
-@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-@ContextConfiguration(initializers = SpringSessionMongoReactiveApplication.Initializer.class)
public class AttributeTests {
@LocalServerPort
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/spring-session-sample-boot-mongodb-traditional.gradle b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/spring-session-sample-boot-mongodb-traditional.gradle
index d9ea2e902..5e91bbcd7 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/spring-session-sample-boot-mongodb-traditional.gradle
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/spring-session-sample-boot-mongodb-traditional.gradle
@@ -1,13 +1,14 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
dependencies {
- implementation project(':spring-session-data-mongodb')
- implementation "org.springframework.boot:spring-boot-starter-web"
- implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
- implementation "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
- implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity6"
+ implementation project(':spring-session-data-mongodb')
+ implementation "org.springframework.boot:spring-boot-starter-web"
+ implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
+ implementation "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
+ implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity6"
implementation "org.springframework.boot:spring-boot-starter-data-mongodb"
implementation "org.springframework.boot:spring-boot-starter-security"
+ implementation "org.springframework.boot:spring-boot-testcontainers"
implementation "org.testcontainers:mongodb"
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoTraditionalBoot.java b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoTraditionalBoot.java
index a63334ddf..686c97467 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoTraditionalBoot.java
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/SpringSessionMongoTraditionalBoot.java
@@ -16,57 +16,18 @@
package org.springframework.session.mongodb.examples;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.testcontainers.containers.MongoDBContainer;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.MapPropertySource;
/**
* @author Rob Winch
+ * @author Yanming Zhou
*/
@SpringBootApplication
public class SpringSessionMongoTraditionalBoot {
public static void main(String[] args) {
- SpringApplication application = new SpringApplication(SpringSessionMongoTraditionalBoot.class);
- application.addInitializers(new Initializer());
- application.run(args);
- }
-
- /**
- * Use Testcontainers to managed MongoDB through Docker.
- *
- *
- * @see Local
- * Developmenet with Testcontainers
- */
- static class Initializer implements ApplicationContextInitializer {
-
- static MongoDBContainer mongo = new MongoDBContainer("mongo:5.0.11");
-
- private static Map getProperties() {
- mongo.start();
-
- HashMap properties = new HashMap<>();
- properties.put("spring.data.mongodb.host", mongo.getHost());
- properties.put("spring.data.mongodb.port", mongo.getFirstMappedPort() + "");
- return properties;
- }
-
- @Override
- public void initialize(ConfigurableApplicationContext context) {
- ConfigurableEnvironment env = context.getEnvironment();
- env.getPropertySources().addFirst(new MapPropertySource("testcontainers", (Map) getProperties()));
- }
-
+ SpringApplication.run(SpringSessionMongoTraditionalBoot.class, args);
}
}
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/config/MongoDbConfig.java b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/config/MongoDbConfig.java
new file mode 100644
index 000000000..67330bfdb
--- /dev/null
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/main/java/org/springframework/session/mongodb/examples/config/MongoDbConfig.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.mongodb.examples.config;
+
+import org.testcontainers.containers.MongoDBContainer;
+
+import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Yanming Zhou
+ */
+@Configuration(proxyBeanMethods = false)
+public class MongoDbConfig {
+
+ @Bean
+ @ServiceConnection
+ MongoDBContainer mongoDbContainer() {
+ return new MongoDBContainer("mongo:5.0.11");
+ }
+
+}
diff --git a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/test/java/org/springframework/session/mongodb/examples/BootTests.java b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/test/java/org/springframework/session/mongodb/examples/BootTests.java
index c91e193bf..c208098aa 100644
--- a/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/test/java/org/springframework/session/mongodb/examples/BootTests.java
+++ b/spring-session-samples/spring-session-sample-boot-mongodb-traditional/src/test/java/org/springframework/session/mongodb/examples/BootTests.java
@@ -21,7 +21,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
@@ -30,11 +29,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.session.mongodb.examples.pages.HomePage;
import org.springframework.session.mongodb.examples.pages.LoginPage;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
@@ -42,11 +38,10 @@
/**
* @author Pool Dolorier
+ * @author Yanming Zhou
*/
-@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
-@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
-@ContextConfiguration(initializers = SpringSessionMongoTraditionalBoot.Initializer.class)
+@SpringBootTest
public class BootTests {
@Autowired
From c625e8a5bc6e987b8b7dfc31675ac32b7d1ac0aa Mon Sep 17 00:00:00 2001
From: limo520
Date: Mon, 25 Sep 2023 11:04:30 +0800
Subject: [PATCH 039/579] Update RedisIndexedSessionRepository javadoc
it seems there is a missing ":" in RedisIndexedSessionRepository javadoc.
---
.../session/data/redis/RedisIndexedSessionRepository.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
index 7cdbb1b39..70fe7982c 100644
--- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
+++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
@@ -102,7 +102,7 @@
* APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
* EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
* SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
- * EXPIRE spring:session:expirations1439245080000 2100
+ * EXPIRE spring:session:expirations:1439245080000 2100
*
*
* Saving a Session
@@ -237,7 +237,7 @@
*
*
* SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
- * EXPIRE spring:session:expirations1439245080000 2100
+ * EXPIRE spring:session:expirations:1439245080000 2100
*
*
*
From 5dff8acbf354741d861deb11d1229fdd8d872647 Mon Sep 17 00:00:00 2001
From: 70825
Date: Sat, 2 Sep 2023 09:51:48 +0900
Subject: [PATCH 040/579] Update reference site link
---
README.adoc | 2 +-
spring-session-docs/modules/ROOT/pages/guides/java-jdbc.adoc | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.adoc b/README.adoc
index 41badb511..158b80fd8 100644
--- a/README.adoc
+++ b/README.adoc
@@ -25,7 +25,7 @@ Additional Spring Session modules can be found in the https://github.com/spring-
== Getting Started
-We recommend you visit the https://docs.spring.io/spring-session/docs/current/reference/html5/#samples[Spring Session Reference] and look through the "Samples and Guides" section to see which one best suits your needs.
+We recommend you visit the https://docs.spring.io/spring-session/reference/[Spring Session Reference] and look through the "Samples and Guides" section to see which one best suits your needs.
== Samples
diff --git a/spring-session-docs/modules/ROOT/pages/guides/java-jdbc.adoc b/spring-session-docs/modules/ROOT/pages/guides/java-jdbc.adoc
index 98feb40b9..be5ccc9d0 100644
--- a/spring-session-docs/modules/ROOT/pages/guides/java-jdbc.adoc
+++ b/spring-session-docs/modules/ROOT/pages/guides/java-jdbc.adoc
@@ -100,7 +100,7 @@ We configure the H2 database to create database tables by using the SQL script t
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
====
-For additional information on how to configure data access related concerns, see the https://docs.spring.io/spring/docs/{spring-core-version}/spring-framework-reference/data-access.html[Spring Framework Reference Documentation].
+For additional information on how to configure data access related concerns, see the https://docs.spring.io/spring/docs/{spring-core-version}/reference/html/data-access.html[Spring Framework Reference Documentation].
== Java Servlet Container Initialization
From 11ce4b45619e6f0abeeec6b09fc8e43c074105e4 Mon Sep 17 00:00:00 2001
From: Yanming Zhou
Date: Wed, 23 Aug 2023 10:39:53 +0800
Subject: [PATCH 041/579] Polishing
1. fix deprecations
2. fix generics
3. use lambda instead of anonymous class
4. mark some fields as final
---
.../gradle/maven/PublishLocalPlugin.java | 20 ++---
.../convention/JavadocApiPluginITest.java | 3 +-
.../convention/SpringMavenPluginITest.java | 3 +-
...pringSessionBackedSessionRegistryTest.java | 3 +-
.../http/OnCommittedResponseWrapperTests.java | 2 +-
.../SpringSessionWebSessionStoreTests.java | 2 +-
.../WebSocketRegistryListenerTests.java | 6 +-
...sionRepositoryMessageInterceptorTests.java | 2 +-
...bDeleteJacksonSessionVerificationTest.java | 88 ++++++++++---------
.../mongo/MongoDbLogoutVerificationTest.java | 27 +++---
.../session/data/mongo/MongoSession.java | 10 +--
.../RedisSessionExpirationPolicyTests.java | 2 +-
.../redis/RedisSessionRepositoryTests.java | 6 +-
...exedHttpSessionConfigurationMockTests.java | 2 +-
...azelcastIndexedSessionRepositoryTests.java | 5 +-
.../JdbcIndexedSessionRepositoryTests.java | 1 +
.../JdbcHttpSessionConfigurationTests.java | 8 +-
.../java/sample/config/WebSecurityConfig.java | 4 +-
18 files changed, 96 insertions(+), 98 deletions(-)
diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java
index 54f9e4971..34dcaeda6 100644
--- a/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java
+++ b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java
@@ -1,9 +1,7 @@
package org.springframework.gradle.maven;
-import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
import org.gradle.api.publish.PublishingExtension;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
@@ -12,18 +10,12 @@
public class PublishLocalPlugin implements Plugin {
@Override
public void apply(Project project) {
- project.getPlugins().withType(MavenPublishPlugin.class).all(new Action() {
- @Override
- public void execute(MavenPublishPlugin mavenPublish) {
- PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
- publishing.getRepositories().maven(new Action() {
- @Override
- public void execute(MavenArtifactRepository maven) {
- maven.setName("local");
- maven.setUrl(new File(project.getRootProject().getBuildDir(), "publications/repos"));
- }
- });
- }
+ project.getPlugins().withType(MavenPublishPlugin.class).all(mavenPublish -> {
+ PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
+ publishing.getRepositories().maven(maven -> {
+ maven.setName("local");
+ maven.setUrl(new File(project.getRootProject().getBuildDir(), "publications/repos"));
+ });
});
}
}
diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginITest.java b/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginITest.java
index ae43f3871..30bc5f07a 100644
--- a/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginITest.java
+++ b/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginITest.java
@@ -9,6 +9,7 @@
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
+import java.nio.charset.Charset;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
@@ -30,7 +31,7 @@ public void multiModuleApi() throws Exception {
File allClasses = new File(testKit.getRootDir(), "build/api/allclasses-noframe.html");
File index = new File(testKit.getRootDir(), "build/api/allclasses-index.html");
File listing = allClasses.exists() ? allClasses : index;
- String listingText = FileUtils.readFileToString(listing);
+ String listingText = FileUtils.readFileToString(listing, Charset.defaultCharset());
assertThat(listingText).contains("sample/Api.html");
assertThat(listingText).contains("sample/Impl.html");
assertThat(listingText).doesNotContain("sample/Sample.html");
diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/SpringMavenPluginITest.java b/buildSrc/src/test/java/io/spring/gradle/convention/SpringMavenPluginITest.java
index 3f0855dd5..ef94edb85 100644
--- a/buildSrc/src/test/java/io/spring/gradle/convention/SpringMavenPluginITest.java
+++ b/buildSrc/src/test/java/io/spring/gradle/convention/SpringMavenPluginITest.java
@@ -9,6 +9,7 @@
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
@@ -56,6 +57,6 @@ public void signArchivesWhenInMemory() throws Exception {
}
public String getSigningKey() throws Exception {
- return IOUtils.toString(getClass().getResource("/test-private.pgp"));
+ return IOUtils.toString(getClass().getResource("/test-private.pgp"), Charset.defaultCharset());
}
}
diff --git a/spring-session-core/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java b/spring-session-core/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java
index 35ef46c50..03041464a 100644
--- a/spring-session-core/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java
+++ b/spring-session-core/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java
@@ -30,6 +30,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.quality.Strictness;
import org.springframework.security.core.AuthenticatedPrincipal;
import org.springframework.security.core.Authentication;
@@ -154,7 +155,7 @@ void expireNow() {
private Session createSession(String sessionId, String userName, Instant lastAccessed) {
MapSession session = new MapSession(sessionId);
session.setLastAccessedTime(lastAccessed);
- Authentication authentication = mock(Authentication.class, withSettings().lenient());
+ Authentication authentication = mock(Authentication.class, withSettings().strictness(Strictness.LENIENT));
given(authentication.getName()).willReturn(userName);
SecurityContextImpl securityContext = new SecurityContextImpl();
securityContext.setAuthentication(authentication);
diff --git a/spring-session-core/src/test/java/org/springframework/session/web/http/OnCommittedResponseWrapperTests.java b/spring-session-core/src/test/java/org/springframework/session/web/http/OnCommittedResponseWrapperTests.java
index 953e0551f..7efd19014 100644
--- a/spring-session-core/src/test/java/org/springframework/session/web/http/OnCommittedResponseWrapperTests.java
+++ b/spring-session-core/src/test/java/org/springframework/session/web/http/OnCommittedResponseWrapperTests.java
@@ -39,7 +39,7 @@ class OnCommittedResponseWrapperTests {
private static final String NL = "\r\n";
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
HttpServletResponse delegate;
@Mock
diff --git a/spring-session-core/src/test/java/org/springframework/session/web/server/session/SpringSessionWebSessionStoreTests.java b/spring-session-core/src/test/java/org/springframework/session/web/server/session/SpringSessionWebSessionStoreTests.java
index 6f14e514b..877e8b263 100644
--- a/spring-session-core/src/test/java/org/springframework/session/web/server/session/SpringSessionWebSessionStoreTests.java
+++ b/spring-session-core/src/test/java/org/springframework/session/web/server/session/SpringSessionWebSessionStoreTests.java
@@ -47,7 +47,7 @@
@ExtendWith(MockitoExtension.class)
class SpringSessionWebSessionStoreTests {
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
private ReactiveSessionRepository sessionRepository;
@Mock
diff --git a/spring-session-core/src/test/java/org/springframework/session/web/socket/handler/WebSocketRegistryListenerTests.java b/spring-session-core/src/test/java/org/springframework/session/web/socket/handler/WebSocketRegistryListenerTests.java
index ec4ed1947..5c5d93f25 100644
--- a/spring-session-core/src/test/java/org/springframework/session/web/socket/handler/WebSocketRegistryListenerTests.java
+++ b/spring-session-core/src/test/java/org/springframework/session/web/socket/handler/WebSocketRegistryListenerTests.java
@@ -48,13 +48,13 @@
@ExtendWith(MockitoExtension.class)
class WebSocketRegistryListenerTests {
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
private WebSocketSession wsSession;
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
private WebSocketSession wsSession2;
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
private Message message;
@Mock
diff --git a/spring-session-core/src/test/java/org/springframework/session/web/socket/server/SessionRepositoryMessageInterceptorTests.java b/spring-session-core/src/test/java/org/springframework/session/web/socket/server/SessionRepositoryMessageInterceptorTests.java
index ae576207a..5334039ac 100644
--- a/spring-session-core/src/test/java/org/springframework/session/web/socket/server/SessionRepositoryMessageInterceptorTests.java
+++ b/spring-session-core/src/test/java/org/springframework/session/web/socket/server/SessionRepositoryMessageInterceptorTests.java
@@ -55,7 +55,7 @@
@ExtendWith(MockitoExtension.class)
class SessionRepositoryMessageInterceptorTests {
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
SessionRepository sessionRepository;
@Mock
diff --git a/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbDeleteJacksonSessionVerificationTest.java b/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbDeleteJacksonSessionVerificationTest.java
index 8e3973405..1ee791ef8 100644
--- a/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbDeleteJacksonSessionVerificationTest.java
+++ b/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbDeleteJacksonSessionVerificationTest.java
@@ -36,6 +36,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
@@ -72,57 +73,62 @@ void setUp() {
void logoutShouldDeleteOldSessionFromMongoDB() {
// 1. Login and capture the SESSION cookie value.
-
+ // @formatter:off
FluxExchangeResult loginResult = this.client.post().uri("/login")
- .contentType(MediaType.APPLICATION_FORM_URLENCODED) //
- .body(BodyInserters //
- .fromFormData("username", "admin") //
- .with("password", "password")) //
- .exchange() //
+ .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .body(BodyInserters
+ .fromFormData("username", "admin")
+ .with("password", "password"))
+ .exchange()
.returnResult(String.class);
+ // @formatter:on
AssertionsForClassTypes.assertThat(loginResult.getResponseHeaders().getLocation()).isEqualTo(URI.create("/"));
String originalSessionId = loginResult.getResponseCookies().getFirst("SESSION").getValue();
// 2. Fetch a protected resource using the SESSION cookie.
-
- this.client.get().uri("/hello") //
- .cookie("SESSION", originalSessionId) //
- .exchange() //
- .expectStatus().isOk() //
- .returnResult(String.class).getResponseBody() //
- .as(StepVerifier::create) //
- .expectNext("HelloWorld") //
+ // @formatter:off
+ this.client.get().uri("/hello")
+ .cookie("SESSION", originalSessionId)
+ .exchange()
+ .expectStatus().isOk()
+ .returnResult(String.class).getResponseBody()
+ .as(StepVerifier::create)
+ .expectNext("HelloWorld")
.verifyComplete();
+ // @formatter:on
// 3. Logout using the SESSION cookie, and capture the new SESSION cookie.
-
- String newSessionId = this.client.post().uri("/logout") //
- .cookie("SESSION", originalSessionId) //
- .exchange() //
- .expectStatus().isFound() //
+ // @formatter:off
+ String newSessionId = this.client.post().uri("/logout")
+ .cookie("SESSION", originalSessionId)
+ .exchange()
+ .expectStatus().isFound()
.returnResult(String.class).getResponseCookies().getFirst("SESSION").getValue();
+ // @formatter:on
AssertionsForClassTypes.assertThat(newSessionId).isNotEqualTo(originalSessionId);
// 4. Verify the new SESSION cookie is not yet authorized.
-
- this.client.get().uri("/hello") //
- .cookie("SESSION", newSessionId) //
- .exchange() //
- .expectStatus().isFound() //
+ // @formatter:off
+ this.client.get().uri("/hello")
+ .cookie("SESSION", newSessionId)
+ .exchange()
+ .expectStatus().isFound()
.expectHeader()
.value(HttpHeaders.LOCATION, (value) -> AssertionsForClassTypes.assertThat(value).isEqualTo("/login"));
+ // @formatter:on
// 5. Verify the original SESSION cookie no longer works.
-
- this.client.get().uri("/hello") //
- .cookie("SESSION", originalSessionId) //
- .exchange() //
- .expectStatus().isFound() //
+ // @formatter:off
+ this.client.get().uri("/hello")
+ .cookie("SESSION", originalSessionId)
+ .exchange()
+ .expectStatus().isFound()
.expectHeader()
.value(HttpHeaders.LOCATION, (value) -> AssertionsForClassTypes.assertThat(value).isEqualTo("/login"));
+ // @formatter:on
}
@RestController
@@ -141,24 +147,24 @@ static class SecurityConfig {
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
- return http //
- .logout()//
- /**/.and() //
- .formLogin() //
- /**/.and() //
- .csrf().disable() //
- .authorizeExchange() //
- .anyExchange().authenticated() //
- /**/.and() //
+ // @formatter:off
+ return http
+ .logout(Customizer.withDefaults())
+ .formLogin(Customizer.withDefaults())
+ .csrf((csrf) -> csrf.disable())
+ .authorizeExchange((ae) -> ae.anyExchange().authenticated())
.build();
+ // @formatter:on
}
@Bean
MapReactiveUserDetailsService userDetailsService() {
- return new MapReactiveUserDetailsService(User.withUsername("admin") //
- .password("{noop}password") //
- .roles("USER,ADMIN") //
+ // @formatter:off
+ return new MapReactiveUserDetailsService(User.withUsername("admin")
+ .password("{noop}password")
+ .roles("USER,ADMIN")
.build());
+ // @formatter:on
}
@Bean
diff --git a/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbLogoutVerificationTest.java b/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbLogoutVerificationTest.java
index 17fb4eac9..49de55f19 100644
--- a/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbLogoutVerificationTest.java
+++ b/spring-session-data-mongodb/src/integration-test/java/org/springframework/session/data/mongo/MongoDbLogoutVerificationTest.java
@@ -36,6 +36,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
@@ -141,26 +142,24 @@ static class SecurityConfig {
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
-
- return http //
- .logout()//
- /**/.and() //
- .formLogin() //
- /**/.and() //
- .csrf().disable() //
- .authorizeExchange() //
- .anyExchange().authenticated() //
- /**/.and() //
+ // @formatter:off
+ return http
+ .logout(Customizer.withDefaults())
+ .formLogin(Customizer.withDefaults())
+ .csrf((csrf) -> csrf.disable())
+ .authorizeExchange((ae) -> ae.anyExchange().authenticated())
.build();
+ // @formatter:on
}
@Bean
MapReactiveUserDetailsService userDetailsService() {
-
- return new MapReactiveUserDetailsService(User.withUsername("admin") //
- .password("{noop}password") //
- .roles("USER,ADMIN") //
+ // @formatter:off
+ return new MapReactiveUserDetailsService(User.withUsername("admin")
+ .password("{noop}password")
+ .roles("USER,ADMIN")
.build());
+ // @formatter:on
}
}
diff --git a/spring-session-data-mongodb/src/main/java/org/springframework/session/data/mongo/MongoSession.java b/spring-session-data-mongodb/src/main/java/org/springframework/session/data/mongo/MongoSession.java
index 25ee1c4db..d3bb813e8 100644
--- a/spring-session-data-mongodb/src/main/java/org/springframework/session/data/mongo/MongoSession.java
+++ b/spring-session-data-mongodb/src/main/java/org/springframework/session/data/mongo/MongoSession.java
@@ -48,15 +48,14 @@ class MongoSession implements Session {
* NOTE: This was originally stored in unicode format. Delomboking the code caused it
* to get converted to another encoding, which isn't supported on all systems, so we
* migrated back to unicode. The same character is being represented ensuring binary
- * compatibility.
- *
- * See https://www.compart.com/en/unicode/U+F607
+ * compatibility. See https://www.compart.com/en/unicode/U+F607
*/
private static final char DOT_COVER_CHAR = '\uF607';
private String id;
- private String originalSessionId;
+ private final String originalSessionId;
private long createdMillis = System.currentTimeMillis();
@@ -66,7 +65,7 @@ class MongoSession implements Session {
private Date expireAt;
- private Map attrs = new HashMap<>();
+ private final Map attrs = new HashMap<>();
private transient SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();
@@ -135,6 +134,7 @@ public String changeSessionId() {
@Override
@Nullable
+ @SuppressWarnings("unchecked")
public T getAttribute(String attributeName) {
return (T) this.attrs.get(coverDot(attributeName));
}
diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java
index af4719684..2249d49db 100644
--- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java
+++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java
@@ -49,7 +49,7 @@ class RedisSessionExpirationPolicyTests {
// Wed Apr 15 10:27:32 CDT 2015
private static final Long ONE_MINUTE_AGO = 1429111652346L;
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
RedisOperations sessionRedisOperations;
@Mock
diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java
index 6f930b727..9e1de8e10 100644
--- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java
+++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java
@@ -59,7 +59,7 @@ class RedisSessionRepositoryTests {
private static final String TEST_SESSION_KEY = getSessionKey(TEST_SESSION_ID);
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
private RedisOperations sessionRedisOperations;
@Mock
@@ -311,7 +311,6 @@ void save_SessionNotExists_ShouldThrowException() {
}
@Test
- @SuppressWarnings("unchecked")
void findById_SessionExists_ShouldReturnSession() {
Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS);
given(this.sessionHashOperations.entries(eq(TEST_SESSION_KEY)))
@@ -334,7 +333,6 @@ void findById_SessionExists_ShouldReturnSession() {
}
@Test
- @SuppressWarnings("unchecked")
void findById_SessionExistsAndIsExpired_ShouldReturnNull() {
given(this.sessionHashOperations.entries(eq(TEST_SESSION_KEY)))
.willReturn(mapOf(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
@@ -410,7 +408,7 @@ private static Instant getExpiry(RedisSession session) {
.plusSeconds(session.getMaxInactiveInterval().getSeconds());
}
- private static Map mapOf(Object... objects) {
+ private static Map mapOf(Object... objects) {
Map result = new HashMap<>();
if (objects != null) {
for (int i = 0; i < objects.length; i += 2) {
diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisIndexedHttpSessionConfigurationMockTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisIndexedHttpSessionConfigurationMockTests.java
index 7e2ca4df5..7794120f9 100644
--- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisIndexedHttpSessionConfigurationMockTests.java
+++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/config/annotation/web/http/RedisIndexedHttpSessionConfigurationMockTests.java
@@ -37,7 +37,7 @@
@ExtendWith(MockitoExtension.class)
class RedisIndexedHttpSessionConfigurationMockTests {
- @Mock(lenient = true)
+ @Mock(strictness = Mock.Strictness.LENIENT)
RedisConnectionFactory factory;
@Mock
diff --git a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java
index 5865ce8cf..243dcaef6 100644
--- a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java
+++ b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java
@@ -61,14 +61,15 @@
* @author Vedran Pavic
* @author Aleksandar Stojsavljevic
*/
+@SuppressWarnings("unchecked")
class HazelcastIndexedSessionRepositoryTests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
- private HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
+ private final HazelcastInstance hazelcastInstance = mock(HazelcastInstance.class);
@SuppressWarnings("unchecked")
- private IMap sessions = mock(IMap.class);
+ private final IMap sessions = mock(IMap.class);
private HazelcastIndexedSessionRepository repository;
diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
index 159e5982a..e9e086c1f 100644
--- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
+++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
@@ -790,6 +790,7 @@ void setSessionIdGeneratorWhenNullThenThrowsException() {
}
@Test
+ @SuppressWarnings("unchecked")
void findByIdWhenChangeSessionIdThenUsesSessionIdGenerator() {
this.repository.setSessionIdGenerator(() -> "test");
Session saved = this.repository.new JdbcSession(new MapSession(), "primaryKey", false);
diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java
index 8d1ccead4..f8dadf5dd 100644
--- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java
+++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java
@@ -72,13 +72,11 @@ class JdbcHttpSessionConfigurationTests {
private static final String CLEANUP_CRON_EXPRESSION = "0 0 * * * *";
- private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@AfterEach
void closeContext() {
- if (this.context != null) {
- this.context.close();
- }
+ this.context.close();
}
@Test
@@ -407,7 +405,7 @@ static class CustomMaxInactiveIntervalInSecondsAnnotationConfiguration {
static class CustomMaxInactiveIntervalInSecondsSetterConfiguration extends JdbcHttpSessionConfiguration {
CustomMaxInactiveIntervalInSecondsSetterConfiguration() {
- setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
+ setMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
diff --git a/spring-session-samples/spring-session-sample-boot-websocket/src/main/java/sample/config/WebSecurityConfig.java b/spring-session-samples/spring-session-sample-boot-websocket/src/main/java/sample/config/WebSecurityConfig.java
index 1c767a8c1..81871ec15 100644
--- a/spring-session-samples/spring-session-sample-boot-websocket/src/main/java/sample/config/WebSecurityConfig.java
+++ b/spring-session-samples/spring-session-sample-boot-websocket/src/main/java/sample/config/WebSecurityConfig.java
@@ -21,7 +21,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -29,7 +29,7 @@
import org.springframework.security.web.SecurityFilterChain;
@Configuration
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
public class WebSecurityConfig {
// @formatter:off
From 24390d824be13dc71877cff3856738261e609196 Mon Sep 17 00:00:00 2001
From: Marcus Da Coregio
Date: Wed, 4 Oct 2023 10:29:30 -0300
Subject: [PATCH 042/579] Upgrade mockito to 5.5.0
Closes gh-2462
---
gradle/dependency-management.gradle | 2 +-
spring-session-core/spring-session-core.gradle | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle
index 0fd1d7b4b..548818e82 100644
--- a/gradle/dependency-management.gradle
+++ b/gradle/dependency-management.gradle
@@ -3,7 +3,7 @@ dependencyManagement {
mavenBom "io.projectreactor:reactor-bom:$reactorVersion"
mavenBom 'com.fasterxml.jackson:jackson-bom:2.15.2'
mavenBom 'org.junit:junit-bom:5.10.0-RC1'
- mavenBom 'org.mockito:mockito-bom:4.11.0'
+ mavenBom 'org.mockito:mockito-bom:5.5.0'
mavenBom 'org.springframework:spring-framework-bom:6.1.0-M2'
mavenBom 'org.springframework.data:spring-data-bom:2023.1.0-M1'
mavenBom 'org.springframework.security:spring-security-bom:6.2.0-M1'
diff --git a/spring-session-core/spring-session-core.gradle b/spring-session-core/spring-session-core.gradle
index d3b11a2bf..cf558e0d8 100644
--- a/spring-session-core/spring-session-core.gradle
+++ b/spring-session-core/spring-session-core.gradle
@@ -19,7 +19,6 @@ dependencies {
testImplementation "io.projectreactor:reactor-test"
testImplementation "org.mockito:mockito-core"
testImplementation "org.mockito:mockito-junit-jupiter"
- testImplementation "org.mockito:mockito-inline"
testImplementation "edu.umd.cs.mtc:multithreadedtc"
testImplementation "org.springframework:spring-test"
testImplementation "org.assertj:assertj-core"
From 3fe23375ded7413820f0c990960dfd8c5d5d22c9 Mon Sep 17 00:00:00 2001
From: Marcus Da Coregio
Date: Mon, 25 Sep 2023 08:23:48 -0300
Subject: [PATCH 043/579] Allow Customizing Redis Session Mapper
Closes gh-2021
---
...veRedisSessionRepositoryKeyMissITests.java | 150 +++++++++++++++++
...IndexedSessionRepositoryKeyMissITests.java | 158 ++++++++++++++++++
.../RedisSessionRepositoryKeyMissITests.java | 157 +++++++++++++++++
.../redis/ReactiveRedisSessionRepository.java | 27 ++-
.../redis/RedisIndexedSessionRepository.java | 26 ++-
.../data/redis/RedisSessionMapper.java | 22 +--
.../data/redis/RedisSessionRepository.java | 20 ++-
.../data/redis/RedisSessionMapperTests.java | 30 ++--
.../ROOT/pages/configuration/redis.adoc | 136 +++++++++++++++
.../modules/ROOT/pages/whats-new.adoc | 1 +
10 files changed, 687 insertions(+), 40 deletions(-)
create mode 100644 spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryKeyMissITests.java
create mode 100644 spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryKeyMissITests.java
create mode 100644 spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryKeyMissITests.java
diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryKeyMissITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryKeyMissITests.java
new file mode 100644
index 000000000..9b5dc26cc
--- /dev/null
+++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryKeyMissITests.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.data.redis;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import reactor.core.publisher.Mono;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.ReactiveHashOperations;
+import org.springframework.data.redis.core.ReactiveRedisOperations;
+import org.springframework.session.MapSession;
+import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
+import org.springframework.session.data.redis.ReactiveRedisSessionRepository.RedisSession;
+import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.willAnswer;
+import static org.mockito.Mockito.spy;
+
+/**
+ * Key miss error tests for {@link ReactiveRedisSessionRepository}
+ *
+ * @author Marcus da Coregio
+ * @see Related
+ * GitHub Issue
+ */
+@ExtendWith(SpringExtension.class)
+class ReactiveRedisSessionRepositoryKeyMissITests extends AbstractRedisITests {
+
+ private ReactiveRedisSessionRepository sessionRepository;
+
+ private ReactiveRedisOperations spyOperations;
+
+ AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaThenThrowIllegalStateException() {
+ this.context.register(Config.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ ReactiveHashOperations opsForHash = spy(this.spyOperations.opsForHash());
+ given(this.spyOperations.opsForHash()).willReturn(opsForHash);
+ willAnswer((invocation) -> this.sessionRepository.deleteById(session.getId())
+ .then((Mono) invocation.callRealMethod())).given(opsForHash).putAll(any(), any());
+
+ this.sessionRepository.save(session).block();
+ assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.findById(session.getId()).block())
+ .withMessage("creationTime key must not be null");
+ }
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaAndSafeMapperThenSessionIsNull() {
+ this.context.register(RedisSessionMapperConfig.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ ReactiveHashOperations opsForHash = spy(this.spyOperations.opsForHash());
+ given(this.spyOperations.opsForHash()).willReturn(opsForHash);
+ willAnswer((invocation) -> this.sessionRepository.deleteById(session.getId())
+ .then((Mono) invocation.callRealMethod())).given(opsForHash).putAll(any(), any());
+
+ this.sessionRepository.save(session).block();
+ assertThat(this.sessionRepository.findById(session.getId()).block()).isNull();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void refreshAndPrepareFields() {
+ this.context.refresh();
+ this.sessionRepository = this.context.getBean(ReactiveRedisSessionRepository.class);
+ ReactiveRedisOperations redisOperations = (ReactiveRedisOperations) ReflectionTestUtils
+ .getField(this.sessionRepository, "sessionRedisOperations");
+ this.spyOperations = spy(redisOperations);
+ ReflectionTestUtils.setField(this.sessionRepository, "sessionRedisOperations", this.spyOperations);
+ }
+
+ private RedisSession createAndSaveSession(Instant lastAccessedTime) {
+ RedisSession session = this.sessionRepository.createSession().block();
+ session.setLastAccessedTime(lastAccessedTime);
+ session.setAttribute("attribute1", "value1");
+ this.sessionRepository.save(session).block();
+ return this.sessionRepository.findById(session.getId()).block();
+ }
+
+ @Configuration
+ @EnableRedisWebSession
+ static class Config extends BaseConfig {
+
+ }
+
+ @Configuration
+ @EnableRedisWebSession
+ static class RedisSessionMapperConfig extends BaseConfig {
+
+ @Bean
+ ReactiveSessionRepositoryCustomizer redisSessionRepositoryCustomizer() {
+ return (redisSessionRepository) -> redisSessionRepository
+ .setRedisSessionMapper(new SafeRedisSessionMapper(redisSessionRepository));
+ }
+
+ }
+
+ static class SafeRedisSessionMapper implements BiFunction, Mono> {
+
+ private final RedisSessionMapper delegate = new RedisSessionMapper();
+
+ private final ReactiveRedisSessionRepository sessionRepository;
+
+ SafeRedisSessionMapper(ReactiveRedisSessionRepository sessionRepository) {
+ this.sessionRepository = sessionRepository;
+ }
+
+ @Override
+ public Mono apply(String sessionId, Map map) {
+ return Mono.fromSupplier(() -> this.delegate.apply(sessionId, map)).onErrorResume(
+ IllegalStateException.class,
+ (ex) -> this.sessionRepository.deleteById(sessionId).then(Mono.empty()));
+ }
+
+ }
+
+}
diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryKeyMissITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryKeyMissITests.java
new file mode 100644
index 000000000..774c3d244
--- /dev/null
+++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryKeyMissITests.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.data.redis;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.BoundHashOperations;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.session.MapSession;
+import org.springframework.session.config.SessionRepositoryCustomizer;
+import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisIndexedHttpSession;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.willAnswer;
+import static org.mockito.Mockito.spy;
+
+/**
+ * Key miss error tests for {@link RedisIndexedSessionRepository}
+ *
+ * @author Marcus da Coregio
+ * @see Related
+ * GitHub Issue
+ */
+@ExtendWith(SpringExtension.class)
+class RedisIndexedSessionRepositoryKeyMissITests extends AbstractRedisITests {
+
+ private RedisIndexedSessionRepository sessionRepository;
+
+ private RedisOperations spyOperations;
+
+ AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaThenThrowIllegalStateException() {
+ this.context.register(Config.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ BoundHashOperations opsForHash = spy(this.spyOperations.boundHashOps(anyString()));
+ given(this.spyOperations.boundHashOps(anyString())).willReturn(opsForHash);
+ willAnswer((invocation) -> {
+ this.sessionRepository.deleteById(session.getId());
+ return invocation.callRealMethod();
+ }).given(opsForHash).putAll(any());
+
+ this.sessionRepository.save(session);
+ assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.findById(session.getId()))
+ .withMessage("creationTime key must not be null");
+ }
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaAndSafeMapperThenSessionIsNull() {
+ this.context.register(RedisSessionMapperConfig.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ BoundHashOperations opsForHash = spy(this.spyOperations.boundHashOps(anyString()));
+ given(this.spyOperations.boundHashOps(anyString())).willReturn(opsForHash);
+ willAnswer((invocation) -> {
+ this.sessionRepository.deleteById(session.getId());
+ return invocation.callRealMethod();
+ }).given(opsForHash).putAll(any());
+
+ this.sessionRepository.save(session);
+ assertThat(this.sessionRepository.findById(session.getId())).isNull();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void refreshAndPrepareFields() {
+ this.context.refresh();
+ this.sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
+ RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils
+ .getField(this.sessionRepository, "sessionRedisOperations");
+ this.spyOperations = spy(redisOperations);
+ ReflectionTestUtils.setField(this.sessionRepository, "sessionRedisOperations", this.spyOperations);
+ }
+
+ private RedisSession createAndSaveSession(Instant lastAccessedTime) {
+ RedisSession session = this.sessionRepository.createSession();
+ session.setLastAccessedTime(lastAccessedTime);
+ session.setAttribute("attribute1", "value1");
+ this.sessionRepository.save(session);
+ return this.sessionRepository.findById(session.getId());
+ }
+
+ @Configuration
+ @EnableRedisIndexedHttpSession
+ static class Config extends BaseConfig {
+
+ }
+
+ @Configuration
+ @EnableRedisIndexedHttpSession
+ static class RedisSessionMapperConfig extends BaseConfig {
+
+ @Bean
+ SessionRepositoryCustomizer redisSessionRepositoryCustomizer() {
+ return (redisSessionRepository) -> redisSessionRepository.setRedisSessionMapper(
+ new SafeRedisSessionMapper(redisSessionRepository.getSessionRedisOperations()));
+ }
+
+ }
+
+ static class SafeRedisSessionMapper implements BiFunction, MapSession> {
+
+ private final RedisSessionMapper delegate = new RedisSessionMapper();
+
+ private final RedisOperations redisOperations;
+
+ SafeRedisSessionMapper(RedisOperations redisOperations) {
+ this.redisOperations = redisOperations;
+ }
+
+ @Override
+ public MapSession apply(String sessionId, Map map) {
+ try {
+ return this.delegate.apply(sessionId, map);
+ }
+ catch (IllegalStateException ex) {
+ this.redisOperations.delete("spring:session:sessions:" + sessionId);
+ return null;
+ }
+ }
+
+ }
+
+}
diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryKeyMissITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryKeyMissITests.java
new file mode 100644
index 000000000..f636d6205
--- /dev/null
+++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryKeyMissITests.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.data.redis;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.session.MapSession;
+import org.springframework.session.config.SessionRepositoryCustomizer;
+import org.springframework.session.data.redis.RedisSessionRepository.RedisSession;
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.willAnswer;
+import static org.mockito.Mockito.spy;
+
+/**
+ * Key miss error tests for {@link RedisSessionRepository}
+ *
+ * @author Marcus da Coregio
+ * @see Related
+ * GitHub Issue
+ */
+@ExtendWith(SpringExtension.class)
+class RedisSessionRepositoryKeyMissITests extends AbstractRedisITests {
+
+ private RedisSessionRepository sessionRepository;
+
+ private RedisOperations spyOperations;
+
+ AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaThenThrowIllegalStateException() {
+ this.context.register(Config.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ HashOperations opsForHash = spy(this.spyOperations.opsForHash());
+ given(this.spyOperations.opsForHash()).willReturn(opsForHash);
+ willAnswer((invocation) -> {
+ this.sessionRepository.deleteById(session.getId());
+ return invocation.callRealMethod();
+ }).given(opsForHash).putAll(any(), any());
+
+ this.sessionRepository.save(session);
+ assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.findById(session.getId()))
+ .withMessage("creationTime key must not be null");
+ }
+
+ @Test
+ void findByIdWhenSessionDeletedWhileSavingDeltaAndSafeMapperThenSessionIsNull() {
+ this.context.register(RedisSessionMapperConfig.class);
+ refreshAndPrepareFields();
+ RedisSession session = createAndSaveSession(Instant.now());
+ session.setAttribute("new", "value");
+
+ HashOperations opsForHash = spy(this.spyOperations.opsForHash());
+ given(this.spyOperations.opsForHash()).willReturn(opsForHash);
+ willAnswer((invocation) -> {
+ this.sessionRepository.deleteById(session.getId());
+ return invocation.callRealMethod();
+ }).given(opsForHash).putAll(any(), any());
+
+ this.sessionRepository.save(session);
+ assertThat(this.sessionRepository.findById(session.getId())).isNull();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void refreshAndPrepareFields() {
+ this.context.refresh();
+ this.sessionRepository = this.context.getBean(RedisSessionRepository.class);
+ RedisOperations redisOperations = (RedisOperations) ReflectionTestUtils
+ .getField(this.sessionRepository, "sessionRedisOperations");
+ this.spyOperations = spy(redisOperations);
+ ReflectionTestUtils.setField(this.sessionRepository, "sessionRedisOperations", this.spyOperations);
+ }
+
+ private RedisSession createAndSaveSession(Instant lastAccessedTime) {
+ RedisSession session = this.sessionRepository.createSession();
+ session.setLastAccessedTime(lastAccessedTime);
+ session.setAttribute("attribute1", "value1");
+ this.sessionRepository.save(session);
+ return this.sessionRepository.findById(session.getId());
+ }
+
+ @Configuration
+ @EnableRedisHttpSession
+ static class Config extends BaseConfig {
+
+ }
+
+ @Configuration
+ @EnableRedisHttpSession
+ static class RedisSessionMapperConfig extends BaseConfig {
+
+ @Bean
+ SessionRepositoryCustomizer redisSessionRepositoryCustomizer() {
+ return (redisSessionRepository) -> redisSessionRepository
+ .setRedisSessionMapper(new SafeRedisSessionMapper(redisSessionRepository));
+ }
+
+ }
+
+ static class SafeRedisSessionMapper implements BiFunction, MapSession> {
+
+ private final RedisSessionMapper delegate = new RedisSessionMapper();
+
+ private final RedisSessionRepository sessionRepository;
+
+ SafeRedisSessionMapper(RedisSessionRepository sessionRepository) {
+ this.sessionRepository = sessionRepository;
+ }
+
+ @Override
+ public MapSession apply(String sessionId, Map map) {
+ try {
+ return this.delegate.apply(sessionId, map);
+ }
+ catch (IllegalStateException ex) {
+ this.sessionRepository.deleteById(sessionId);
+ return null;
+ }
+ }
+
+ }
+
+}
diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java
index 84460599a..a24db32d8 100644
--- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java
+++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java
@@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiFunction;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
@@ -66,6 +67,8 @@ public class ReactiveRedisSessionRepository
private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();
+ private BiFunction, Mono> redisSessionMapper = new RedisSessionMapperAdapter();
+
/**
* Create a new {@link ReactiveRedisSessionRepository} instance.
* @param sessionRedisOperations the {@link ReactiveRedisOperations} to use for
@@ -154,7 +157,7 @@ public Mono findById(String id) {
return this.sessionRedisOperations.opsForHash().entries(sessionKey)
.collectMap((e) -> e.getKey().toString(), Map.Entry::getValue)
.filter((map) -> !map.isEmpty())
- .map(new RedisSessionMapper(id))
+ .flatMap((map) -> this.redisSessionMapper.apply(id, map))
.filter((session) -> !session.isExpired())
.map((session) -> new RedisSession(session, false))
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
@@ -186,6 +189,16 @@ public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {
this.sessionIdGenerator = sessionIdGenerator;
}
+ /**
+ * Set the {@link BiFunction} used to convert a {@link Map} to a {@link MapSession}.
+ * @param redisSessionMapper the mapper to use, cannot be null
+ * @since 3.2
+ */
+ public void setRedisSessionMapper(BiFunction, Mono> redisSessionMapper) {
+ Assert.notNull(redisSessionMapper, "redisSessionMapper cannot be null");
+ this.redisSessionMapper = redisSessionMapper;
+ }
+
/**
* A custom implementation of {@link Session} that uses a {@link MapSession} as the
* basis for its mapping. It keeps track of any attributes that have changed. When
@@ -349,4 +362,16 @@ private Mono saveChangeSessionId() {
}
+ private static final class RedisSessionMapperAdapter
+ implements BiFunction, Mono> {
+
+ private final RedisSessionMapper mapper = new RedisSessionMapper();
+
+ @Override
+ public Mono apply(String sessionId, Map map) {
+ return Mono.fromSupplier(() -> this.mapper.apply(sessionId, map));
+ }
+
+ }
+
}
diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
index 70fe7982c..9b08c4824 100644
--- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
+++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java
@@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiFunction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -326,6 +327,8 @@ public class RedisIndexedSessionRepository
private SessionIdGenerator sessionIdGenerator = UuidSessionIdGenerator.getInstance();
+ private BiFunction, MapSession> redisSessionMapper = new RedisSessionMapper();
+
/**
* Creates a new instance. For an example, refer to the class level javadoc.
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
@@ -523,8 +526,8 @@ private RedisSession getSession(String id, boolean allowExpired) {
if ((entries == null) || entries.isEmpty()) {
return null;
}
- MapSession loaded = new RedisSessionMapper(id).apply(entries);
- if (!allowExpired && loaded.isExpired()) {
+ MapSession loaded = this.redisSessionMapper.apply(id, entries);
+ if (loaded == null || (!allowExpired && loaded.isExpired())) {
return null;
}
RedisSession result = new RedisSession(loaded, false);
@@ -568,9 +571,11 @@ public void onMessage(Message message, byte[] pattern) {
String sessionId = channel.substring(channel.lastIndexOf(":") + 1);
@SuppressWarnings("unchecked")
Map entries = (Map) this.defaultSerializer.deserialize(message.getBody());
- MapSession loaded = new RedisSessionMapper(sessionId).apply(entries);
- RedisSession session = new RedisSession(loaded, false);
- handleCreated(session);
+ MapSession loaded = this.redisSessionMapper.apply(sessionId, entries);
+ if (loaded != null) {
+ RedisSession session = new RedisSession(loaded, false);
+ handleCreated(session);
+ }
return;
}
@@ -730,6 +735,17 @@ public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {
this.sessionIdGenerator = sessionIdGenerator;
}
+ /**
+ * Set the {@link BiFunction} used to map {@link MapSession} to a
+ * {@link ReactiveRedisSessionRepository.RedisSession}.
+ * @param redisSessionMapper the mapper to use, cannot be null
+ * @since 3.2
+ */
+ public void setRedisSessionMapper(BiFunction, MapSession> redisSessionMapper) {
+ Assert.notNull(redisSessionMapper, "redisSessionMapper cannot be null");
+ this.redisSessionMapper = redisSessionMapper;
+ }
+
/**
* A custom implementation of {@link Session} that uses a {@link MapSession} as the
* basis for its mapping. It keeps track of any attributes that have changed. When
diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java
index 476d03d38..ab2f926c8 100644
--- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java
+++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2019 the original author or authors.
+ * Copyright 2014-2023 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.
@@ -19,6 +19,7 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.session.MapSession;
@@ -30,9 +31,10 @@
* {@link MapSession}.
*
* @author Vedran Pavic
+ * @author Marcus da Coregio
* @since 2.2.0
*/
-final class RedisSessionMapper implements Function