Compare commits
154 Commits
2.2.0.M2
...
2.2.6.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d5ba54289 | ||
|
|
45e6c3a0ba | ||
|
|
9f142ed49d | ||
|
|
0758dd737d | ||
|
|
f7852f379a | ||
|
|
61d3d56e6d | ||
|
|
7d929457e4 | ||
|
|
3d266960ba | ||
|
|
4d96099e03 | ||
|
|
e0103f62d6 | ||
|
|
304b496caf | ||
|
|
c1e3c2831d | ||
|
|
e228279683 | ||
|
|
70c14368e5 | ||
|
|
7f9abc822f | ||
|
|
f426f91574 | ||
|
|
316fe09f72 | ||
|
|
00338e23dd | ||
|
|
c6c2d53204 | ||
|
|
bf5dcda905 | ||
|
|
32ec8b2b28 | ||
|
|
3268d1f790 | ||
|
|
380a1e81ac | ||
|
|
5be4141103 | ||
|
|
4ebf18dc4e | ||
|
|
b5c67736ad | ||
|
|
dfab409f30 | ||
|
|
a0a394d17f | ||
|
|
1a98f25fdb | ||
|
|
1afb5d5a17 | ||
|
|
365a244a9b | ||
|
|
0b4140d892 | ||
|
|
78a85789c9 | ||
|
|
59350ed559 | ||
|
|
811e156a9c | ||
|
|
05a9903348 | ||
|
|
d8ae336b24 | ||
|
|
315112f2a2 | ||
|
|
e859da6d27 | ||
|
|
028bae1f11 | ||
|
|
234cb6dd88 | ||
|
|
43101308ec | ||
|
|
089f6b92de | ||
|
|
c6d129a5a5 | ||
|
|
938fd3c2e5 | ||
|
|
45bb0f9b0c | ||
|
|
cddd84d564 | ||
|
|
6931d40e6e | ||
|
|
3b672787f3 | ||
|
|
c0ee52b33b | ||
|
|
68f8641233 | ||
|
|
e7b2af47e1 | ||
|
|
1ad6cbd7f8 | ||
|
|
195af52d0b | ||
|
|
bc9d5f1299 | ||
|
|
3a4345eb6a | ||
|
|
6c41dea893 | ||
|
|
ee1d5b3b3c | ||
|
|
89a4255679 | ||
|
|
6d2e51a0b9 | ||
|
|
798d398d9b | ||
|
|
085554f56b | ||
|
|
45b3b35db7 | ||
|
|
2d06e1159c | ||
|
|
927008bdc8 | ||
|
|
30588dc3c8 | ||
|
|
2f79da00dc | ||
|
|
e2abe36fa8 | ||
|
|
456fd3adb4 | ||
|
|
bd0f474b5b | ||
|
|
e5a3933cb6 | ||
|
|
71e5cc857a | ||
|
|
df455ddc89 | ||
|
|
eceeaa665d | ||
|
|
e6c54d8a75 | ||
|
|
c88456a183 | ||
|
|
f5abd55394 | ||
|
|
b9fd3666b5 | ||
|
|
e06ea36ad5 | ||
|
|
0a1701233e | ||
|
|
47a4873199 | ||
|
|
bd36e115a8 | ||
|
|
ec82336477 | ||
|
|
feaf8780a8 | ||
|
|
b357a76ce3 | ||
|
|
2c6f22afb0 | ||
|
|
34306fd3a0 | ||
|
|
a6c1d8eb1d | ||
|
|
e48b46a2d5 | ||
|
|
8cc8fbb7fd | ||
|
|
96715e04f2 | ||
|
|
121a633a40 | ||
|
|
bf31a9b04b | ||
|
|
a209d436d1 | ||
|
|
6c76a1ccdd | ||
|
|
c974eeb188 | ||
|
|
3b5dadb07f | ||
|
|
3e6b3fda0f | ||
|
|
840da7fb5a | ||
|
|
560ee5ff4f | ||
|
|
072348e28f | ||
|
|
99dfdda7b7 | ||
|
|
18b097d9c7 | ||
|
|
702a35fac6 | ||
|
|
df3e4c5bc1 | ||
|
|
f746233255 | ||
|
|
f6c82f1eee | ||
|
|
bcdd05a0bc | ||
|
|
5d26ab4df4 | ||
|
|
e55d86f5e2 | ||
|
|
fe480b338c | ||
|
|
4b13392430 | ||
|
|
e5d9ce6ead | ||
|
|
bc1ef4359a | ||
|
|
98fa5ed52d | ||
|
|
44468134aa | ||
|
|
c14c621da6 | ||
|
|
6ceca18248 | ||
|
|
229ca09f10 | ||
|
|
36b82d52f8 | ||
|
|
dbeb33fd9d | ||
|
|
23bf92a086 | ||
|
|
2e0c347a3a | ||
|
|
972537a3d5 | ||
|
|
99c4f5bc32 | ||
|
|
89f8127763 | ||
|
|
63f1013a07 | ||
|
|
a988b062c3 | ||
|
|
09368243aa | ||
|
|
903fa10861 | ||
|
|
a883e2ee77 | ||
|
|
f68e569bec | ||
|
|
593c126c03 | ||
|
|
033d6eecae | ||
|
|
24b9d24e18 | ||
|
|
46bc4b0957 | ||
|
|
8cb85618c2 | ||
|
|
1ca9daccb4 | ||
|
|
0731e7f2d0 | ||
|
|
8d0d757e46 | ||
|
|
e2f1fe5446 | ||
|
|
07b9433540 | ||
|
|
a6f6042831 | ||
|
|
099be441dd | ||
|
|
17d029d34d | ||
|
|
aab9b39a6b | ||
|
|
090d882f98 | ||
|
|
d91d09567f | ||
|
|
ab5b0e3a32 | ||
|
|
549a2b7f0b | ||
|
|
050ff7538b | ||
|
|
e8e65ac09f | ||
|
|
822db7fbbf | ||
|
|
0eaeb98b0c |
18
.travis.yml
18
.travis.yml
@@ -1,20 +1,16 @@
|
||||
language: java
|
||||
|
||||
sudo: required
|
||||
|
||||
services: docker
|
||||
|
||||
jdk: oraclejdk8
|
||||
|
||||
language: java
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
services:
|
||||
- docker
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
install: true
|
||||
|
||||
script: ./gradlew clean build --refresh-dependencies --no-daemon
|
||||
script: ./gradlew clean check --no-daemon --stacktrace
|
||||
|
||||
66
Jenkinsfile
vendored
66
Jenkinsfile
vendored
@@ -8,16 +8,21 @@ properties([
|
||||
def SUCCESS = hudson.model.Result.SUCCESS.toString()
|
||||
currentBuild.result = SUCCESS
|
||||
|
||||
def ARTIFACTORY_CREDENTIALS = usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')
|
||||
|
||||
try {
|
||||
parallel check: {
|
||||
stage('Check') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
label 'spring-session'
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew clean check --no-daemon --refresh-dependencies --stacktrace'
|
||||
withCredentials([ARTIFACTORY_CREDENTIALS]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew clean check -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@@ -31,44 +36,6 @@ try {
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk9: {
|
||||
stage('JDK 9') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk9'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk9'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk10'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk11: {
|
||||
stage('JDK 11') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
@@ -76,8 +43,10 @@ try {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk11'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies --stacktrace'
|
||||
withCredentials([ARTIFACTORY_CREDENTIALS]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk11'}"]) {
|
||||
sh './gradlew clean test integrationTest -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@@ -94,8 +63,10 @@ try {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'openjdk12'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies --stacktrace'
|
||||
withCredentials([ARTIFACTORY_CREDENTIALS]) {
|
||||
withEnv(["JAVA_HOME=${tool 'openjdk12'}"]) {
|
||||
sh './gradlew clean test integrationTest -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@@ -117,9 +88,10 @@ 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')]) {
|
||||
withCredentials([ARTIFACTORY_CREDENTIALS]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew deployArtifacts finalizeDeployArtifacts --no-daemon --refresh-dependencies --stacktrace -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'
|
||||
sh './gradlew deployArtifacts --no-daemon --stacktrace -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'
|
||||
sh './gradlew finalizeDeployArtifacts --no-daemon --stacktrace -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +113,7 @@ try {
|
||||
try {
|
||||
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew deployDocs --no-daemon --refresh-dependencies --stacktrace -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME'
|
||||
sh './gradlew deployDocs --no-daemon --stacktrace -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
build.gradle
34
build.gradle
@@ -4,7 +4,7 @@ buildscript {
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.2.0.M2'
|
||||
springBootVersion = '2.2.13.RELEASE'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -13,28 +13,40 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.25.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.27.1.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.2.RELEASE'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
group = 'org.springframework.session'
|
||||
description = 'Spring Session'
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'io.spring.javaformat'
|
||||
|
||||
plugins.withType(JavaPlugin) {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
tasks.withType(Test) {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
allprojects { project ->
|
||||
project.repositories { repos ->
|
||||
all { repo ->
|
||||
if (!repo.url.toString().startsWith("https://repo.spring.io/")) {
|
||||
return;
|
||||
}
|
||||
repo.credentials {
|
||||
username = artifactoryUsername
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/suppressions.xml"/>
|
||||
</module>
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks"/>
|
||||
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
|
||||
<module name="io.spring.javaformat.checkstyle.check.SpringJUnit5Check"/>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck">
|
||||
<property name="regexp" value="true"/>
|
||||
<property name="illegalPkgs"
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
<suppress files="[\\/]spring-session-docs[\\/]" checks="InnerTypeLast"/>
|
||||
<suppress files="[\\/]spring-session-samples[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]spring-session-samples[\\/].+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files="SessionRepositoryFilterTests\.java" checks="SpringLambda"/>
|
||||
<suppress files="CookieSerializer\.java" checks="SpringMethodVisibility"/>
|
||||
</suppressions>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
|
||||
<local-check-config name="Spring Session Checkstyle" location="${configDir}/checkstyle.xml" type="external" description="">
|
||||
<property name="configDir" value="${configDir}"/>
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Spring Session Checkstyle" local="true">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
@@ -1,295 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="Spring Session Java Conventions" version="12">
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="90"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="90"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
@@ -1,391 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.fieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.fieldSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.localPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.localSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deadCode=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deprecation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
|
||||
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
|
||||
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
|
||||
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
|
||||
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default
|
||||
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
|
||||
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=default
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
|
||||
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
|
||||
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.nullReference=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
|
||||
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
|
||||
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
|
||||
org.eclipse.jdt.core.compiler.processAnnotations=disabled
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_header=false
|
||||
org.eclipse.jdt.core.formatter.comment.format_html=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_source_code=false
|
||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.line_length=90
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.lineSplit=90
|
||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||
org.eclipse.jdt.core.formatter.tabulation.char=tab
|
||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||
org.eclipse.jdt.core.formatter.use_on_off_tags=true
|
||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||
org.eclipse.jdt.core.javaFormatter=org.springframework.ide.eclipse.jdt.formatter.javaformatter
|
||||
File diff suppressed because one or more lines are too long
128
etc/idea/codeStyleConfig.xml
Normal file
128
etc/idea/codeStyleConfig.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<code_scheme name="SpringSession" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false"/>
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="USE_TAB_CHARACTER" value="true"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="50"/>
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500"/>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="java" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="javax" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="org.springframework" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="true"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="RIGHT_MARGIN" value="90"/>
|
||||
<option name="ENABLE_JAVADOC_FORMATTING" value="false"/>
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false"/>
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false"/>
|
||||
<option name="JD_KEEP_EMPTY_LINES" value="false"/>
|
||||
<GroovyCodeStyleSettings>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500"/>
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500"/>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<emptyLine/>
|
||||
<package name="javax" withSubpackages="true" static="false"/>
|
||||
<package name="java" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="org.springframework" withSubpackages="true"
|
||||
static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="true"/>
|
||||
</value>
|
||||
</option>
|
||||
</GroovyCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="CLASS_NAMES_IN_JAVADOC" value="3"/>
|
||||
<option name="INSERT_INNER_CLASS_IMPORTS" value="true"/>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="50"/>
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500"/>
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value/>
|
||||
</option>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="java" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="javax" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="org.springframework" withSubpackages="true"
|
||||
static="false"/>
|
||||
<emptyLine/>
|
||||
<package name="" withSubpackages="true" static="true"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="ENABLE_JAVADOC_FORMATTING" value="false"/>
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false"/>
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false"/>
|
||||
<option name="JD_KEEP_INVALID_TAGS" value="false"/>
|
||||
<option name="JD_KEEP_EMPTY_LINES" value="false"/>
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" withSubpackages="false" static="false"/>
|
||||
<package name="kotlinx.android.synthetic" withSubpackages="false"
|
||||
static="false"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="20"/>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="20"/>
|
||||
</JetCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true"/>
|
||||
</XML>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false"/>
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
|
||||
<option name="BLANK_LINES_AROUND_FIELD" value="1"/>
|
||||
<option name="BLANK_LINES_AROUND_FIELD_IN_INTERFACE" value="1"/>
|
||||
<option name="ELSE_ON_NEW_LINE" value="true"/>
|
||||
<option name="CATCH_ON_NEW_LINE" value="true"/>
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true"/>
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true"/>
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/>
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true"/>
|
||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true"/>
|
||||
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true"/>
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
@@ -1 +1,3 @@
|
||||
version=2.2.0.M2
|
||||
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
version=2.2.6.RELEASE
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-M2'
|
||||
mavenBom 'org.junit:junit-bom:5.4.2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.0.M3'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RC1'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.M3'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.11.3'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-SR19'
|
||||
mavenBom 'org.junit:junit-bom:5.5.2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.14.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-SR13'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.10.RELEASE'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.15.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.12.1') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.12.12') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
|
||||
dependency 'com.h2database:h2:1.4.199'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.2.2.jre8'
|
||||
dependency 'com.zaxxer:HikariCP:3.3.1'
|
||||
dependency 'com.h2database:h2:1.4.200'
|
||||
dependency 'com.ibm.db2:jcc:11.5.0.0'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.4.1.jre8'
|
||||
dependency 'com.oracle.ojdbc:ojdbc8:19.3.0.0'
|
||||
dependency 'com.zaxxer:HikariCP:3.4.5'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.7.RELEASE'
|
||||
dependency 'io.lettuce:lettuce-core:5.2.2.RELEASE'
|
||||
dependency 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
dependency 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
dependency 'mysql:mysql-connector-java:8.0.16'
|
||||
dependency 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.23'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.12.2'
|
||||
dependency 'org.hsqldb:hsqldb:2.5.0'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.4.1'
|
||||
dependency 'org.mockito:mockito-core:2.28.2'
|
||||
dependency 'org.postgresql:postgresql:42.2.5'
|
||||
dependency 'org.assertj:assertj-core:3.13.2'
|
||||
dependency 'org.hsqldb:hsqldb:2.5.1'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.4.4'
|
||||
dependency 'org.mockito:mockito-core:3.0.0'
|
||||
dependency 'org.postgresql:postgresql:42.2.16'
|
||||
}
|
||||
}
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
4
gradlew
vendored
4
gradlew
vendored
@@ -125,8 +125,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An {@link IndexResolver} that resolves indexes using multiple @{link IndexResolver}
|
||||
* delegates.
|
||||
*
|
||||
* @param <S> the type of Session being handled
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class DelegatingIndexResolver<S extends Session> implements IndexResolver<S> {
|
||||
|
||||
private final List<IndexResolver<S>> delegates;
|
||||
|
||||
public DelegatingIndexResolver(List<IndexResolver<S>> delegates) {
|
||||
this.delegates = Collections.unmodifiableList(delegates);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public DelegatingIndexResolver(IndexResolver<S>... delegates) {
|
||||
this(Arrays.asList(delegates));
|
||||
}
|
||||
|
||||
public Map<String, String> resolveIndexesFor(S session) {
|
||||
Map<String, String> indexes = new HashMap<>();
|
||||
for (IndexResolver<S> delegate : this.delegates) {
|
||||
indexes.putAll(delegate.resolveIndexesFor(session));
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,8 +27,7 @@ import java.util.Map;
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
extends SessionRepository<S> {
|
||||
public interface FindByIndexNameSessionRepository<S extends Session> extends SessionRepository<S> {
|
||||
|
||||
/**
|
||||
* A session index that contains the current principal name (i.e. username).
|
||||
@@ -44,7 +43,6 @@ public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
/**
|
||||
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
|
||||
* contain the specified index name index value.
|
||||
*
|
||||
* @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.
|
||||
@@ -59,7 +57,6 @@ public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
* 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,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
/**
|
||||
* Supported modes of writing the session to session store.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public enum FlushMode {
|
||||
|
||||
/**
|
||||
* Only writes to session store when {@link SessionRepository#save(Session)} is
|
||||
* invoked. In a web environment this is typically done as soon as the HTTP response
|
||||
* is committed.
|
||||
*/
|
||||
ON_SAVE,
|
||||
|
||||
/**
|
||||
* Writes to session store as soon as possible. For example
|
||||
* {@link SessionRepository#createSession()} will write the session to session store.
|
||||
* Another example is that setting an attribute using
|
||||
* {@link Session#setAttribute(String, Object)} will also write to session store
|
||||
* immediately.
|
||||
*/
|
||||
IMMEDIATE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving the {@link Session}'s indexes.
|
||||
*
|
||||
* @param <S> the type of Session being handled
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
* @see FindByIndexNameSessionRepository
|
||||
*/
|
||||
public interface IndexResolver<S extends Session> {
|
||||
|
||||
/**
|
||||
* Resolve indexes for the session.
|
||||
* @param session the session
|
||||
* @return a map of resolved indexes, never {@code null}
|
||||
*/
|
||||
Map<String, String> resolveIndexesFor(S session);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,15 +47,20 @@ import java.util.UUID;
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class MapSession implements Session, Serializable {
|
||||
|
||||
/**
|
||||
* Default {@link #setMaxInactiveInterval(Duration)} (30 minutes).
|
||||
*/
|
||||
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
|
||||
|
||||
private String id;
|
||||
|
||||
private final String originalId;
|
||||
|
||||
private Map<String, Object> sessionAttrs = new HashMap<>();
|
||||
|
||||
private Instant creationTime = Instant.now();
|
||||
|
||||
private Instant lastAccessedTime = this.creationTime;
|
||||
|
||||
/**
|
||||
@@ -70,12 +75,10 @@ public final class MapSession implements Session, Serializable {
|
||||
this(generateId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified id. This is preferred to the default
|
||||
* constructor when the id is known to prevent unnecessary consumption on entropy
|
||||
* which can be slow.
|
||||
*
|
||||
* @param id the identifier to use
|
||||
*/
|
||||
public MapSession(String id) {
|
||||
@@ -85,7 +88,6 @@ public final class MapSession implements Session, Serializable {
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link Session}.
|
||||
*
|
||||
* @param session the {@link Session} to initialize this {@link Session} with. Cannot
|
||||
* be null.
|
||||
*/
|
||||
@@ -95,8 +97,7 @@ public final class MapSession implements Session, Serializable {
|
||||
}
|
||||
this.id = session.getId();
|
||||
this.originalId = this.id;
|
||||
this.sessionAttrs = new HashMap<>(
|
||||
session.getAttributeNames().size());
|
||||
this.sessionAttrs = new HashMap<>(session.getAttributeNames().size());
|
||||
for (String attrName : session.getAttributeNames()) {
|
||||
Object attrValue = session.getAttribute(attrName);
|
||||
if (attrValue != null) {
|
||||
@@ -205,7 +206,6 @@ public final class MapSession implements Session, Serializable {
|
||||
* Sets the identifier for this {@link Session}. The id should be a secure random
|
||||
* generated value to prevent malicious users from guessing this value. The default is
|
||||
* a secure random generated identifier.
|
||||
*
|
||||
* @param id the identifier for this session.
|
||||
*/
|
||||
public void setId(String id) {
|
||||
@@ -227,4 +227,5 @@ public final class MapSession implements Session, Serializable {
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 7160779239673823561L;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,7 +49,6 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link java.util.Map}. This allows
|
||||
* injecting a distributed {@link java.util.Map}.
|
||||
*
|
||||
* @param sessions the {@link java.util.Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapSessionRepository(Map<String, Session> sessions) {
|
||||
@@ -99,8 +98,7 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
public MapSession createSession() {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* {@link IndexResolver} to resolve the principal name from session attribute named
|
||||
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} or Spring Security
|
||||
* context stored in the session under {@code SPRING_SECURITY_CONTEXT} attribute.
|
||||
*
|
||||
* @param <S> the type of Session being handled
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class PrincipalNameIndexResolver<S extends Session> extends SingleIndexResolver<S> {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private static final Expression expression = new SpelExpressionParser().parseExpression("authentication?.name");
|
||||
|
||||
public PrincipalNameIndexResolver() {
|
||||
super(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
}
|
||||
|
||||
public String resolveIndexValueFor(S session) {
|
||||
String principalName = session.getAttribute(getIndexName());
|
||||
if (principalName != null) {
|
||||
return principalName;
|
||||
}
|
||||
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (authentication != null) {
|
||||
return expression.getValue(authentication, String.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,7 +51,6 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows injecting a
|
||||
* distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public ReactiveMapSessionRepository(Map<String, Session> sessions) {
|
||||
@@ -101,8 +100,7 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
|
||||
return Mono.defer(() -> {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
return Mono.just(result);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,6 @@ public interface ReactiveSessionRepository<S extends Session> {
|
||||
* persisted. For example, the implementation returned might keep track of the changes
|
||||
* ensuring that only the delta needs to be persisted on a save.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@link Session} that is capable of being persisted by this
|
||||
* {@link ReactiveSessionRepository}
|
||||
*/
|
||||
@@ -51,7 +50,6 @@ public interface ReactiveSessionRepository<S extends Session> {
|
||||
* returning a {@link Session} that immediately persists any changes. In this case,
|
||||
* this method may not actually do anything.
|
||||
* </p>
|
||||
*
|
||||
* @param session the {@link Session} to save
|
||||
* @return indicator of operation completion
|
||||
*/
|
||||
@@ -60,7 +58,6 @@ public interface ReactiveSessionRepository<S extends Session> {
|
||||
/**
|
||||
* Gets the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
*
|
||||
* @param id the {@link Session#getId()} to lookup
|
||||
* @return the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
/**
|
||||
* Supported modes of tracking and saving session changes to session store.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public enum SaveMode {
|
||||
|
||||
/**
|
||||
* Save only changes made to session, for instance using
|
||||
* {@link Session#setAttribute(String, Object)}. In highly concurrent environments,
|
||||
* this mode minimizes the risk of attributes being overwritten during processing of
|
||||
* parallel requests.
|
||||
*/
|
||||
ON_SET_ATTRIBUTE,
|
||||
|
||||
/**
|
||||
* Same as {@link #ON_SET_ATTRIBUTE} with addition of saving attributes that have been
|
||||
* read using {@link Session#getAttribute(String)}.
|
||||
*/
|
||||
ON_GET_ATTRIBUTE,
|
||||
|
||||
/**
|
||||
* Always save all session attributes, regardless of the interaction with the session.
|
||||
* In highly concurrent environments, this mode increases the risk of attributes being
|
||||
* overwritten during processing of parallel requests.
|
||||
*/
|
||||
ALWAYS
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,13 +32,13 @@ public interface Session {
|
||||
|
||||
/**
|
||||
* Gets a unique string that identifies the {@link Session}.
|
||||
*
|
||||
* @return a unique string that identifies the {@link Session}
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Changes the session id. After invoking the {@link #getId()} will return a new identifier.
|
||||
* Changes the session id. After invoking the {@link #getId()} will return a new
|
||||
* identifier.
|
||||
* @return the new session id which {@link #getId()} will now return
|
||||
*/
|
||||
String changeSessionId();
|
||||
@@ -46,7 +46,6 @@ public interface Session {
|
||||
/**
|
||||
* Gets the Object associated with the specified name or null if no Object is
|
||||
* associated to that name.
|
||||
*
|
||||
* @param <T> the return type of the attribute
|
||||
* @param attributeName the name of the attribute to get
|
||||
* @return the Object associated with the specified name or null if no Object is
|
||||
@@ -65,8 +64,7 @@ public interface Session {
|
||||
default <T> T getRequiredAttribute(String name) {
|
||||
T result = getAttribute(name);
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Required attribute '" + name + "' is missing.");
|
||||
throw new IllegalArgumentException("Required attribute '" + name + "' is missing.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -88,7 +86,6 @@ public interface Session {
|
||||
* Gets the attribute names that have a value associated with it. Each value can be
|
||||
* passed into {@link org.springframework.session.Session#getAttribute(String)} to
|
||||
* obtain the attribute value.
|
||||
*
|
||||
* @return the attribute names that have a value associated with it.
|
||||
* @see #getAttribute(String)
|
||||
*/
|
||||
@@ -98,7 +95,6 @@ public interface Session {
|
||||
* Sets the attribute value for the provided attribute name. If the attributeValue is
|
||||
* null, it has the same result as removing the attribute with
|
||||
* {@link org.springframework.session.Session#removeAttribute(String)} .
|
||||
*
|
||||
* @param attributeName the attribute name to set
|
||||
* @param attributeValue the value of the attribute to set. If null, the attribute
|
||||
* will be removed.
|
||||
@@ -113,21 +109,18 @@ public interface Session {
|
||||
|
||||
/**
|
||||
* Gets the time when this session was created.
|
||||
*
|
||||
* @return the time when this session was created.
|
||||
*/
|
||||
Instant getCreationTime();
|
||||
|
||||
/**
|
||||
* Sets the last accessed time.
|
||||
*
|
||||
* @param lastAccessedTime the last accessed time
|
||||
*/
|
||||
void setLastAccessedTime(Instant lastAccessedTime);
|
||||
|
||||
/**
|
||||
* Gets the last time this {@link Session} was accessed.
|
||||
*
|
||||
* @return the last time the client sent a request associated with the session
|
||||
*/
|
||||
Instant getLastAccessedTime();
|
||||
@@ -135,7 +128,6 @@ public interface Session {
|
||||
/**
|
||||
* Sets the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*
|
||||
* @param interval the amount of time that the {@link Session} should be kept alive
|
||||
* between client requests.
|
||||
*/
|
||||
@@ -144,7 +136,6 @@ public interface Session {
|
||||
/**
|
||||
* Gets the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*
|
||||
* @return the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*/
|
||||
@@ -152,7 +143,6 @@ public interface Session {
|
||||
|
||||
/**
|
||||
* Returns true if the session is expired.
|
||||
*
|
||||
* @return true if the session is expired, else false.
|
||||
*/
|
||||
boolean isExpired();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,7 +34,6 @@ public interface SessionRepository<S extends Session> {
|
||||
* persisted. For example, the implementation returned might keep track of the changes
|
||||
* ensuring that only the delta needs to be persisted on a save.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@link Session} that is capable of being persisted by this
|
||||
* {@link SessionRepository}
|
||||
*/
|
||||
@@ -49,7 +48,6 @@ public interface SessionRepository<S extends Session> {
|
||||
* returning a {@link Session} that immediately persists any changes. In this case,
|
||||
* this method may not actually do anything.
|
||||
* </p>
|
||||
*
|
||||
* @param session the {@link Session} to save
|
||||
*/
|
||||
void save(S session);
|
||||
@@ -57,7 +55,6 @@ public interface SessionRepository<S extends Session> {
|
||||
/**
|
||||
* Gets the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
*
|
||||
* @param id the {@link org.springframework.session.Session#getId()} to lookup
|
||||
* @return the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
@@ -70,4 +67,5 @@ public interface SessionRepository<S extends Session> {
|
||||
* @param id the {@link org.springframework.session.Session#getId()} to delete
|
||||
*/
|
||||
void deleteById(String id);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link IndexResolver}s that resolve a single index.
|
||||
*
|
||||
* @param <S> the type of Session being handled
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public abstract class SingleIndexResolver<S extends Session> implements IndexResolver<S> {
|
||||
|
||||
private final String indexName;
|
||||
|
||||
protected SingleIndexResolver(String indexName) {
|
||||
Assert.notNull(indexName, "Index name must not be null");
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
protected String getIndexName() {
|
||||
return this.indexName;
|
||||
}
|
||||
|
||||
public abstract String resolveIndexValueFor(S session);
|
||||
|
||||
public final Map<String, String> resolveIndexesFor(S session) {
|
||||
String indexValue = resolveIndexValueFor(session);
|
||||
return (indexValue != null) ? Collections.singletonMap(this.indexName, indexValue) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.config;
|
||||
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
|
||||
/**
|
||||
* Strategy that can be used to customize the {@link ReactiveSessionRepository} before it
|
||||
* is fully initialized, in particular to tune its configuration.
|
||||
*
|
||||
* @param <T> the {@link ReactiveSessionRepository} type
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReactiveSessionRepositoryCustomizer<T extends ReactiveSessionRepository> {
|
||||
|
||||
/**
|
||||
* Customize the {@link ReactiveSessionRepository}.
|
||||
* @param sessionRepository the {@link ReactiveSessionRepository} to customize
|
||||
*/
|
||||
void customize(T sessionRepository);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.config;
|
||||
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
/**
|
||||
* Strategy that can be used to customize the {@link SessionRepository} before it is fully
|
||||
* initialized, in particular to tune its configuration.
|
||||
*
|
||||
* @param <T> the {@link SessionRepository} type
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SessionRepositoryCustomizer<T extends SessionRepository> {
|
||||
|
||||
/**
|
||||
* Customize the {@link SessionRepository}.
|
||||
* @param sessionRepository the {@link SessionRepository} to customize
|
||||
*/
|
||||
void customize(T sessionRepository);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -74,6 +74,7 @@ import org.springframework.session.events.SessionDestroyedEvent;
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(SpringHttpSessionConfiguration.class)
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableSpringHttpSession {
|
||||
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.1
|
||||
*
|
||||
* @see EnableSpringHttpSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -110,8 +109,7 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
CookieSerializer cookieSerializer = (this.cookieSerializer != null)
|
||||
? this.cookieSerializer
|
||||
CookieSerializer cookieSerializer = (this.cookieSerializer != null) ? this.cookieSerializer
|
||||
: createDefaultCookieSerializer();
|
||||
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
|
||||
}
|
||||
@@ -124,21 +122,16 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
@Bean
|
||||
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
|
||||
SessionRepository<S> sessionRepository) {
|
||||
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
|
||||
sessionRepository);
|
||||
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
|
||||
return sessionRepositoryFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
if (ClassUtils.isPresent(
|
||||
"org.springframework.security.web.authentication.RememberMeServices",
|
||||
null)) {
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if (ClassUtils.isPresent("org.springframework.security.web.authentication.RememberMeServices", null)) {
|
||||
this.usesSpringSessionRememberMeServices = !ObjectUtils
|
||||
.isEmpty(applicationContext
|
||||
.getBeanNamesForType(SpringSessionRememberMeServices.class));
|
||||
.isEmpty(applicationContext.getBeanNamesForType(SpringSessionRememberMeServices.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +163,7 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
sessionCookieConfig = this.servletContext.getSessionCookieConfig();
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
this.logger
|
||||
.warn("Unable to obtain SessionCookieConfig: " + ex.getMessage());
|
||||
this.logger.warn("Unable to obtain SessionCookieConfig: " + ex.getMessage());
|
||||
}
|
||||
if (sessionCookieConfig != null) {
|
||||
if (sessionCookieConfig.getName() != null) {
|
||||
@@ -189,8 +181,7 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
}
|
||||
}
|
||||
if (this.usesSpringSessionRememberMeServices) {
|
||||
cookieSerializer.setRememberMeRequestAttribute(
|
||||
SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
|
||||
cookieSerializer.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
|
||||
}
|
||||
return cookieSerializer;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,9 +23,10 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Add this annotation to a {@code @Configuration} class to configure a {@code WebSessionManager} for a WebFlux
|
||||
* application. This annotation assumes a {@code ReactiveSessionRepository} is defined somewhere in the application
|
||||
* context. If not, it will fail with a clear error message. For example:
|
||||
* Add this annotation to a {@code @Configuration} class to configure a
|
||||
* {@code WebSessionManager} for a WebFlux application. This annotation assumes a
|
||||
* {@code ReactiveSessionRepository} is defined somewhere in the application context. If
|
||||
* not, it will fail with a clear error message. For example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
@@ -39,8 +40,7 @@ import org.springframework.context.annotation.Import;
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
* </code> </pre>
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @since 2.0
|
||||
@@ -49,6 +49,7 @@ import org.springframework.context.annotation.Import;
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(SpringWebSessionConfiguration.class)
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableSpringWebSession {
|
||||
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ import org.springframework.web.server.session.WebSessionIdResolver;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
/**
|
||||
* Wire up a {@link WebSessionManager} using a Reactive {@link ReactiveSessionRepository} from the application context.
|
||||
* Wire up a {@link WebSessionManager} using a Reactive {@link ReactiveSessionRepository}
|
||||
* from the application context.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*
|
||||
* @see EnableSpringWebSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -45,10 +45,11 @@ public class SpringWebSessionConfiguration {
|
||||
private WebSessionIdResolver webSessionIdResolver;
|
||||
|
||||
/**
|
||||
* Configure a {@link WebSessionManager} using a provided {@link ReactiveSessionRepository}.
|
||||
*
|
||||
* Configure a {@link WebSessionManager} using a provided
|
||||
* {@link ReactiveSessionRepository}.
|
||||
* @param repository a bean that implements {@link ReactiveSessionRepository}.
|
||||
* @return a configured {@link WebSessionManager} registered with a preconfigured name.
|
||||
* @return a configured {@link WebSessionManager} registered with a preconfigured
|
||||
* name.
|
||||
*/
|
||||
@Bean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)
|
||||
public WebSessionManager webSessionManager(ReactiveSessionRepository<? extends Session> repository) {
|
||||
@@ -62,4 +63,5 @@ public class SpringWebSessionConfiguration {
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,7 +44,6 @@ public abstract class AbstractSessionEvent extends ApplicationEvent {
|
||||
* Gets the {@link Session} that was destroyed. For some {@link SessionRepository}
|
||||
* implementations it may not be possible to get the original session in which case
|
||||
* this may be null.
|
||||
*
|
||||
* @param <S> the type of Session
|
||||
* @return the expired {@link Session} or null if the data store does not support
|
||||
* obtaining it
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,23 +36,18 @@ import org.springframework.session.SessionRepository;
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3
|
||||
*/
|
||||
class SpringSessionBackedSessionInformation<S extends Session>
|
||||
extends SessionInformation {
|
||||
class SpringSessionBackedSessionInformation<S extends Session> extends SessionInformation {
|
||||
|
||||
static final String EXPIRED_ATTR = SpringSessionBackedSessionInformation.class
|
||||
.getName() + ".EXPIRED";
|
||||
static final String EXPIRED_ATTR = SpringSessionBackedSessionInformation.class.getName() + ".EXPIRED";
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(SpringSessionBackedSessionInformation.class);
|
||||
private static final Log logger = LogFactory.getLog(SpringSessionBackedSessionInformation.class);
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private final SessionRepository<S> sessionRepository;
|
||||
|
||||
SpringSessionBackedSessionInformation(S session,
|
||||
SessionRepository<S> sessionRepository) {
|
||||
super(resolvePrincipal(session), session.getId(),
|
||||
Date.from(session.getLastAccessedTime()));
|
||||
SpringSessionBackedSessionInformation(S session, SessionRepository<S> sessionRepository) {
|
||||
super(resolvePrincipal(session), session.getId(), Date.from(session.getLastAccessedTime()));
|
||||
this.sessionRepository = sessionRepository;
|
||||
Boolean expired = session.getAttribute(EXPIRED_ATTR);
|
||||
if (Boolean.TRUE.equals(expired)) {
|
||||
@@ -62,20 +57,16 @@ class SpringSessionBackedSessionInformation<S extends Session>
|
||||
|
||||
/**
|
||||
* Tries to determine the principal's name from the given Session.
|
||||
*
|
||||
* @param session the session
|
||||
* @return the principal's name, or empty String if it couldn't be determined
|
||||
*/
|
||||
private static String resolvePrincipal(Session session) {
|
||||
String principalName = session
|
||||
.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
String principalName = session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (principalName != null) {
|
||||
return principalName;
|
||||
}
|
||||
SecurityContext securityContext = session
|
||||
.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (securityContext != null
|
||||
&& securityContext.getAuthentication() != null) {
|
||||
SecurityContext securityContext = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (securityContext != null && securityContext.getAuthentication() != null) {
|
||||
return securityContext.getAuthentication().getName();
|
||||
}
|
||||
return "";
|
||||
@@ -84,9 +75,8 @@ class SpringSessionBackedSessionInformation<S extends Session>
|
||||
@Override
|
||||
public void expireNow() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Expiring session " + getSessionId() + " for user '"
|
||||
+ getPrincipal() + "', presumably because maximum allowed concurrent "
|
||||
+ "sessions was exceeded");
|
||||
logger.debug("Expiring session " + getSessionId() + " for user '" + getPrincipal()
|
||||
+ "', presumably because maximum allowed concurrent " + "sessions was exceeded");
|
||||
}
|
||||
super.expireNow();
|
||||
S session = this.sessionRepository.findById(getSessionId());
|
||||
@@ -95,8 +85,7 @@ class SpringSessionBackedSessionInformation<S extends Session>
|
||||
this.sessionRepository.save(session);
|
||||
}
|
||||
else {
|
||||
logger.info("Could not find Session with id " + getSessionId()
|
||||
+ " to mark as expired");
|
||||
logger.info("Could not find Session with id " + getSessionId() + " to mark as expired");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -44,13 +43,11 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3
|
||||
*/
|
||||
public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
implements SessionRegistry {
|
||||
public class SpringSessionBackedSessionRegistry<S extends Session> implements SessionRegistry {
|
||||
|
||||
private final FindByIndexNameSessionRepository<S> sessionRepository;
|
||||
|
||||
public SpringSessionBackedSessionRegistry(
|
||||
FindByIndexNameSessionRepository<S> sessionRepository) {
|
||||
public SpringSessionBackedSessionRegistry(FindByIndexNameSessionRepository<S> sessionRepository) {
|
||||
Assert.notNull(sessionRepository, "sessionRepository cannot be null");
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
@@ -63,16 +60,13 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SessionInformation> getAllSessions(Object principal,
|
||||
boolean includeExpiredSessions) {
|
||||
Collection<S> sessions = this.sessionRepository
|
||||
.findByPrincipalName(name(principal)).values();
|
||||
public List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
|
||||
Collection<S> sessions = this.sessionRepository.findByPrincipalName(name(principal)).values();
|
||||
List<SessionInformation> infos = new ArrayList<>();
|
||||
for (S session : sessions) {
|
||||
if (includeExpiredSessions || !Boolean.TRUE.equals(session
|
||||
.getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))) {
|
||||
infos.add(new SpringSessionBackedSessionInformation<>(session,
|
||||
this.sessionRepository));
|
||||
if (includeExpiredSessions
|
||||
|| !Boolean.TRUE.equals(session.getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))) {
|
||||
infos.add(new SpringSessionBackedSessionInformation<>(session, this.sessionRepository));
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
@@ -82,8 +76,7 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
public SessionInformation getSessionInformation(String sessionId) {
|
||||
S session = this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
return new SpringSessionBackedSessionInformation<>(session,
|
||||
this.sessionRepository);
|
||||
return new SpringSessionBackedSessionInformation<>(session, this.sessionRepository);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -111,19 +104,13 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
|
||||
/**
|
||||
* Derives a String name for the given principal.
|
||||
*
|
||||
* @param principal as provided by Spring Security
|
||||
* @return name of the principal, or its {@code toString()} representation if no name
|
||||
* could be derived
|
||||
*/
|
||||
protected String name(Object principal) {
|
||||
if (principal instanceof UserDetails) {
|
||||
return ((UserDetails) principal).getUsername();
|
||||
}
|
||||
if (principal instanceof Principal) {
|
||||
return ((Principal) principal).getName();
|
||||
}
|
||||
return principal.toString();
|
||||
// We are reusing the logic from AbstractAuthenticationToken#getName
|
||||
return new TestingAuthenticationToken(principal, null).getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,21 +36,19 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class SpringSessionRememberMeServices
|
||||
implements RememberMeServices, LogoutHandler {
|
||||
public class SpringSessionRememberMeServices implements RememberMeServices, LogoutHandler {
|
||||
|
||||
/**
|
||||
* Remember-me login request attribute name.
|
||||
*/
|
||||
public static final String REMEMBER_ME_LOGIN_ATTR = SpringSessionRememberMeServices.class
|
||||
.getName() + "REMEMBER_ME_LOGIN_ATTR";
|
||||
public static final String REMEMBER_ME_LOGIN_ATTR = SpringSessionRememberMeServices.class.getName()
|
||||
+ "REMEMBER_ME_LOGIN_ATTR";
|
||||
|
||||
private static final String DEFAULT_REMEMBERME_PARAMETER = "remember-me";
|
||||
|
||||
private static final int THIRTY_DAYS_SECONDS = 2592000;
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(SpringSessionRememberMeServices.class);
|
||||
private static final Log logger = LogFactory.getLog(SpringSessionRememberMeServices.class);
|
||||
|
||||
private String rememberMeParameterName = DEFAULT_REMEMBERME_PARAMETER;
|
||||
|
||||
@@ -58,25 +56,20 @@ public class SpringSessionRememberMeServices
|
||||
|
||||
private int validitySeconds = THIRTY_DAYS_SECONDS;
|
||||
|
||||
private String sessionAttrToDeleteOnLoginFail = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
|
||||
@Override
|
||||
public final Authentication autoLogin(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void loginFail(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||
logout(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void loginSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication successfulAuthentication) {
|
||||
if (!this.alwaysRemember
|
||||
&& !rememberMeRequested(request, this.rememberMeParameterName)) {
|
||||
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication successfulAuthentication) {
|
||||
if (!this.alwaysRemember && !rememberMeRequested(request, this.rememberMeParameterName)) {
|
||||
logger.debug("Remember-me login not requested.");
|
||||
return;
|
||||
}
|
||||
@@ -103,8 +96,7 @@ public class SpringSessionRememberMeServices
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Did not send remember-me cookie (principal did not set "
|
||||
+ "parameter '" + parameter + "')");
|
||||
logger.debug("Did not send remember-me cookie (principal did not set " + "parameter '" + parameter + "')");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -116,8 +108,7 @@ public class SpringSessionRememberMeServices
|
||||
* @param rememberMeParameterName the request parameter
|
||||
*/
|
||||
public void setRememberMeParameterName(String rememberMeParameterName) {
|
||||
Assert.hasText(rememberMeParameterName,
|
||||
"rememberMeParameterName cannot be empty or null");
|
||||
Assert.hasText(rememberMeParameterName, "rememberMeParameterName cannot be empty or null");
|
||||
this.rememberMeParameterName = rememberMeParameterName;
|
||||
}
|
||||
|
||||
@@ -130,8 +121,7 @@ public class SpringSessionRememberMeServices
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) {
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
logout(request);
|
||||
}
|
||||
|
||||
@@ -139,7 +129,8 @@ public class SpringSessionRememberMeServices
|
||||
logger.debug("Interactive login attempt was unsuccessful.");
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
session.removeAttribute(this.sessionAttrToDeleteOnLoginFail);
|
||||
session.removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterRegistration.Dynamic;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Conventions;
|
||||
@@ -71,8 +70,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
*
|
||||
*/
|
||||
@Order(100)
|
||||
public abstract class AbstractHttpSessionApplicationInitializer
|
||||
implements WebApplicationInitializer {
|
||||
public abstract class AbstractHttpSessionApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
private static final String SERVLET_CONTEXT_PREFIX = "org.springframework.web.servlet.FrameworkServlet.CONTEXT.";
|
||||
|
||||
@@ -98,17 +96,15 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
/**
|
||||
* Creates a new instance that will instantiate the {@link ContextLoaderListener} with
|
||||
* the specified classes.
|
||||
*
|
||||
* @param configurationClasses {@code @Configuration} classes that will be used to
|
||||
* configure the context
|
||||
*/
|
||||
protected AbstractHttpSessionApplicationInitializer(
|
||||
Class<?>... configurationClasses) {
|
||||
protected AbstractHttpSessionApplicationInitializer(Class<?>... configurationClasses) {
|
||||
this.configurationClasses = configurationClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
beforeSessionRepositoryFilter(servletContext);
|
||||
if (this.configurationClasses != null) {
|
||||
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
|
||||
@@ -125,8 +121,7 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
*/
|
||||
private void insertSessionRepositoryFilter(ServletContext servletContext) {
|
||||
String filterName = DEFAULT_FILTER_NAME;
|
||||
DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(
|
||||
filterName);
|
||||
DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(filterName);
|
||||
String contextAttribute = getWebApplicationContextAttribute();
|
||||
if (contextAttribute != null) {
|
||||
springSessionRepositoryFilter.setContextAttribute(contextAttribute);
|
||||
@@ -138,7 +133,6 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
* Inserts the provided {@link Filter}s before existing {@link Filter}s using default
|
||||
* generated names, {@link #getSessionDispatcherTypes()}, and
|
||||
* {@link #isAsyncSessionSupported()}.
|
||||
*
|
||||
* @param servletContext the {@link ServletContext} to use
|
||||
* @param filters the {@link Filter}s to register
|
||||
*/
|
||||
@@ -150,7 +144,6 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
* Inserts the provided {@link Filter}s after existing {@link Filter}s using default
|
||||
* generated names, {@link #getSessionDispatcherTypes()}, and
|
||||
* {@link #isAsyncSessionSupported()}.
|
||||
*
|
||||
* @param servletContext the {@link ServletContext} to use
|
||||
* @param filters the {@link Filter}s to register
|
||||
*/
|
||||
@@ -161,22 +154,18 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
/**
|
||||
* Registers the provided {@link Filter}s using default generated names,
|
||||
* {@link #getSessionDispatcherTypes()}, and {@link #isAsyncSessionSupported()}.
|
||||
*
|
||||
* @param servletContext the {@link ServletContext} to use
|
||||
* @param insertBeforeOtherFilters if true, will insert the provided {@link Filter}s
|
||||
* before other {@link Filter}s. Otherwise, will insert the {@link Filter}s after
|
||||
* other {@link Filter}s.
|
||||
* @param filters the {@link Filter}s to register
|
||||
*/
|
||||
private void registerFilters(ServletContext servletContext,
|
||||
boolean insertBeforeOtherFilters, Filter... filters) {
|
||||
private void registerFilters(ServletContext servletContext, boolean insertBeforeOtherFilters, Filter... filters) {
|
||||
Assert.notEmpty(filters, "filters cannot be null or empty");
|
||||
|
||||
for (Filter filter : filters) {
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"filters cannot contain null values. Got "
|
||||
+ Arrays.asList(filters));
|
||||
throw new IllegalArgumentException("filters cannot contain null values. Got " + Arrays.asList(filters));
|
||||
}
|
||||
String filterName = Conventions.getVariableName(filter);
|
||||
registerFilter(servletContext, insertBeforeOtherFilters, filterName, filter);
|
||||
@@ -186,25 +175,22 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
/**
|
||||
* Registers the provided filter using the {@link #isAsyncSessionSupported()} and
|
||||
* {@link #getSessionDispatcherTypes()}.
|
||||
*
|
||||
* @param servletContext the servlet context
|
||||
* @param insertBeforeOtherFilters should this Filter be inserted before or after
|
||||
* other {@link Filter}
|
||||
* @param filterName the filter name
|
||||
* @param filter the filter
|
||||
*/
|
||||
private void registerFilter(ServletContext servletContext,
|
||||
boolean insertBeforeOtherFilters, String filterName, Filter filter) {
|
||||
private void registerFilter(ServletContext servletContext, boolean insertBeforeOtherFilters, String filterName,
|
||||
Filter filter) {
|
||||
Dynamic registration = servletContext.addFilter(filterName, filter);
|
||||
if (registration == null) {
|
||||
throw new IllegalStateException(
|
||||
"Duplicate Filter registration for '" + filterName
|
||||
+ "'. Check to ensure the Filter is only configured once.");
|
||||
throw new IllegalStateException("Duplicate Filter registration for '" + filterName
|
||||
+ "'. Check to ensure the Filter is only configured once.");
|
||||
}
|
||||
registration.setAsyncSupported(isAsyncSessionSupported());
|
||||
EnumSet<DispatcherType> dispatcherTypes = getSessionDispatcherTypes();
|
||||
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
|
||||
"/*");
|
||||
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters, "/*");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +204,6 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
* {@link ApplicationContext} is used to look up the springSessionRepositoryFilter
|
||||
* bean.
|
||||
* </p>
|
||||
*
|
||||
* @return the {@link DelegatingFilterProxy#getContextAttribute()} or null if the
|
||||
* parent {@link ApplicationContext} should be used
|
||||
*/
|
||||
@@ -241,7 +226,6 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
* name, you can return "dispatcher" from this method to use the DispatcherServlet's
|
||||
* {@link WebApplicationContext}.
|
||||
* </p>
|
||||
*
|
||||
* @return the {@code <servlet-name>} of the DispatcherServlet to use its
|
||||
* {@link WebApplicationContext} or null (default) to use the parent
|
||||
* {@link ApplicationContext}.
|
||||
@@ -271,17 +255,16 @@ public abstract class AbstractHttpSessionApplicationInitializer
|
||||
* @return the {@link DispatcherType} for the filter
|
||||
*/
|
||||
protected EnumSet<DispatcherType> getSessionDispatcherTypes() {
|
||||
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR,
|
||||
DispatcherType.ASYNC);
|
||||
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the springSessionRepositoryFilter should be marked as supporting
|
||||
* asynch. Default is true.
|
||||
*
|
||||
* @return true if springSessionRepositoryFilter should be marked as supporting asynch
|
||||
*/
|
||||
protected boolean isAsyncSessionSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -63,8 +63,8 @@ import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
*/
|
||||
public final class CookieHttpSessionIdResolver implements HttpSessionIdResolver {
|
||||
|
||||
private static final String WRITTEN_SESSION_ID_ATTR = CookieHttpSessionIdResolver.class
|
||||
.getName().concat(".WRITTEN_SESSION_ID_ATTR");
|
||||
private static final String WRITTEN_SESSION_ID_ATTR = CookieHttpSessionIdResolver.class.getName()
|
||||
.concat(".WRITTEN_SESSION_ID_ATTR");
|
||||
|
||||
private CookieSerializer cookieSerializer = new DefaultCookieSerializer();
|
||||
|
||||
@@ -74,14 +74,12 @@ public final class CookieHttpSessionIdResolver implements HttpSessionIdResolver
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionId(HttpServletRequest request, HttpServletResponse response,
|
||||
String sessionId) {
|
||||
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
|
||||
if (sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) {
|
||||
return;
|
||||
}
|
||||
request.setAttribute(WRITTEN_SESSION_ID_ATTR, sessionId);
|
||||
this.cookieSerializer
|
||||
.writeCookieValue(new CookieValue(request, response, sessionId));
|
||||
this.cookieSerializer.writeCookieValue(new CookieValue(request, response, sessionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,7 +89,6 @@ public final class CookieHttpSessionIdResolver implements HttpSessionIdResolver
|
||||
|
||||
/**
|
||||
* Sets the {@link CookieSerializer} to be used.
|
||||
*
|
||||
* @param cookieSerializer the cookieSerializer to set. Cannot be null.
|
||||
*/
|
||||
public void setCookieSerializer(CookieSerializer cookieSerializer) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,7 +32,6 @@ public interface CookieSerializer {
|
||||
|
||||
/**
|
||||
* Writes a given {@link CookieValue} to the provided {@link HttpServletResponse}.
|
||||
*
|
||||
* @param cookieValue the {@link CookieValue} to write to
|
||||
* {@link CookieValue#getResponse()}. Cannot be null.
|
||||
*/
|
||||
@@ -43,7 +42,6 @@ public interface CookieSerializer {
|
||||
* List since there can be multiple {@link Cookie} in a single request with a matching
|
||||
* name. For example, one Cookie may have a path of / and another of /context, but the
|
||||
* path is not transmitted in the request.
|
||||
*
|
||||
* @param request the {@link HttpServletRequest} to read the cookie from. Cannot be
|
||||
* null.
|
||||
* @return the values of all the matching cookies
|
||||
@@ -70,7 +68,6 @@ public interface CookieSerializer {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param request the {@link HttpServletRequest} to use. Useful for determining
|
||||
* the context in which the cookie is set. Cannot be null.
|
||||
* @param response the {@link HttpServletResponse} to use.
|
||||
@@ -78,8 +75,7 @@ public interface CookieSerializer {
|
||||
* modified by the {@link CookieSerializer} when writing to the actual cookie so
|
||||
* long as the original value is returned when the cookie is read.
|
||||
*/
|
||||
public CookieValue(HttpServletRequest request, HttpServletResponse response,
|
||||
String cookieValue) {
|
||||
public CookieValue(HttpServletRequest request, HttpServletResponse response, String cookieValue) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.cookieValue = cookieValue;
|
||||
@@ -108,7 +104,6 @@ public interface CookieSerializer {
|
||||
* The value to be written. This value may be modified by the
|
||||
* {@link CookieSerializer} before written to the cookie. However, the value must
|
||||
* be the same as the original when it is read back in.
|
||||
*
|
||||
* @return the value to be written
|
||||
*/
|
||||
public String getCookieValue() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
@@ -62,6 +63,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
domainValid.set('-');
|
||||
}
|
||||
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
private String cookieName = "SESSION";
|
||||
|
||||
private Boolean useSecureCookie;
|
||||
@@ -97,15 +100,12 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (this.cookieName.equals(cookie.getName())) {
|
||||
String sessionId = (this.useBase64Encoding
|
||||
? base64Decode(cookie.getValue())
|
||||
: cookie.getValue());
|
||||
String sessionId = (this.useBase64Encoding ? base64Decode(cookie.getValue()) : cookie.getValue());
|
||||
if (sessionId == null) {
|
||||
continue;
|
||||
}
|
||||
if (this.jvmRoute != null && sessionId.endsWith(this.jvmRoute)) {
|
||||
sessionId = sessionId.substring(0,
|
||||
sessionId.length() - this.jvmRoute.length());
|
||||
sessionId = sessionId.substring(0, sessionId.length() - this.jvmRoute.length());
|
||||
}
|
||||
matchingCookieValues.add(sessionId);
|
||||
}
|
||||
@@ -124,7 +124,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
public void writeCookieValue(CookieValue cookieValue) {
|
||||
HttpServletRequest request = cookieValue.getRequest();
|
||||
HttpServletResponse response = cookieValue.getResponse();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.cookieName).append('=');
|
||||
String value = getValue(cookieValue);
|
||||
@@ -135,11 +134,9 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
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));
|
||||
ZonedDateTime expires = (maxAge != 0) ? ZonedDateTime.now(this.clock).plusSeconds(maxAge)
|
||||
: Instant.EPOCH.atZone(ZoneOffset.UTC);
|
||||
sb.append("; Expires=").append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
String domain = getDomainName(request);
|
||||
if (domain != null && domain.length() > 0) {
|
||||
@@ -160,7 +157,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
if (this.sameSite != null) {
|
||||
sb.append("; SameSite=").append(this.sameSite);
|
||||
}
|
||||
|
||||
response.addHeader("Set-Cookie", sb.toString());
|
||||
}
|
||||
|
||||
@@ -214,10 +210,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
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));
|
||||
if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
|
||||
throw new IllegalArgumentException("Invalid character in cookie value: " + c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,8 +219,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
private int getMaxAge(CookieValue cookieValue) {
|
||||
int maxAge = cookieValue.getCookieMaxAge();
|
||||
if (maxAge < 0) {
|
||||
if (this.rememberMeRequestAttribute != null && cookieValue.getRequest()
|
||||
.getAttribute(this.rememberMeRequestAttribute) != null) {
|
||||
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
|
||||
@@ -247,8 +241,7 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
while (i < chars.length) {
|
||||
prev = cur;
|
||||
cur = chars[i];
|
||||
if (!domainValid.get(cur)
|
||||
|| ((prev == '.' || prev == -1) && (cur == '.' || cur == '-'))
|
||||
if (!domainValid.get(cur) || ((prev == '.' || prev == -1) && (cur == '.' || cur == '-'))
|
||||
|| (prev == '-' && cur == '.')) {
|
||||
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
|
||||
}
|
||||
@@ -267,10 +260,13 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
void setClock(Clock clock) {
|
||||
this.clock = clock.withZone(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if a Cookie marked as secure should be used. The default is to use the value
|
||||
* of {@link HttpServletRequest#isSecure()}.
|
||||
*
|
||||
* @param useSecureCookie determines if the cookie should be marked as secure.
|
||||
*/
|
||||
public void setUseSecureCookie(boolean useSecureCookie) {
|
||||
@@ -279,7 +275,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
|
||||
/**
|
||||
* Sets if a Cookie marked as HTTP Only should be used. The default is true.
|
||||
*
|
||||
* @param useHttpOnlyCookie determines if the cookie should be marked as HTTP Only.
|
||||
*/
|
||||
public void setUseHttpOnlyCookie(boolean useHttpOnlyCookie) {
|
||||
@@ -296,7 +291,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
/**
|
||||
* Sets the path of the Cookie. The default is to use the context path from the
|
||||
* {@link HttpServletRequest}.
|
||||
*
|
||||
* @param cookiePath the path of the Cookie. If null, the default of the context path
|
||||
* will be used.
|
||||
*/
|
||||
@@ -314,7 +308,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
/**
|
||||
* Sets the maxAge property of the Cookie. The default is to delete the cookie when
|
||||
* the browser is closed.
|
||||
*
|
||||
* @param cookieMaxAge the maxAge property of the Cookie
|
||||
*/
|
||||
public void setCookieMaxAge(int cookieMaxAge) {
|
||||
@@ -325,14 +318,12 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
* Sets an explicit Domain Name. This allow the domain of "example.com" to be used
|
||||
* when the request comes from www.example.com. This allows for sharing the cookie
|
||||
* across subdomains. The default is to use the current domain.
|
||||
*
|
||||
* @param domainName the name of the domain to use. (i.e. "example.com")
|
||||
* @throws IllegalStateException if the domainNamePattern is also set
|
||||
*/
|
||||
public void setDomainName(String domainName) {
|
||||
if (this.domainNamePattern != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot set both domainName and domainNamePattern");
|
||||
throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
this.domainName = domainName;
|
||||
}
|
||||
@@ -362,18 +353,15 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
* <li>localhost - null</li>
|
||||
* <li>127.0.1.1 - null</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param domainNamePattern the case insensitive pattern to extract the domain name
|
||||
* with
|
||||
* @throws IllegalStateException if the domainName is also set
|
||||
*/
|
||||
public void setDomainNamePattern(String domainNamePattern) {
|
||||
if (this.domainName != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot set both domainName and domainNamePattern");
|
||||
throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
this.domainNamePattern = Pattern.compile(domainNamePattern,
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
this.domainNamePattern = Pattern.compile(domainNamePattern, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,7 +378,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
* To use set a custom route on each JVM instance and setup a frontend proxy to
|
||||
* forward all requests to the JVM based on the route.
|
||||
* </p>
|
||||
*
|
||||
* @param jvmRoute the JVM Route to use (i.e. "node01jvmA", "n01ja", etc)
|
||||
*/
|
||||
public void setJvmRoute(String jvmRoute) {
|
||||
@@ -401,7 +388,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
* Set if the Base64 encoding of cookie value should be used. This is valuable in
|
||||
* order to support <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> which
|
||||
* recommends using Base 64 encoding to the cookie value.
|
||||
*
|
||||
* @param useBase64Encoding the flag to indicate whether to use Base64 encoding
|
||||
*/
|
||||
public void setUseBase64Encoding(boolean useBase64Encoding) {
|
||||
@@ -416,8 +402,7 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
*/
|
||||
public void setRememberMeRequestAttribute(String rememberMeRequestAttribute) {
|
||||
if (rememberMeRequestAttribute == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"rememberMeRequestAttribute cannot be null");
|
||||
throw new IllegalArgumentException("rememberMeRequestAttribute cannot be null");
|
||||
}
|
||||
this.rememberMeRequestAttribute = rememberMeRequestAttribute;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -98,13 +98,11 @@ 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
|
||||
public void setSessionId(HttpServletRequest request, HttpServletResponse response,
|
||||
String sessionId) {
|
||||
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
|
||||
response.setHeader(this.headerName, sessionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,11 +65,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public void setSession(S session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public S getSession() {
|
||||
S getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
@@ -142,8 +138,8 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
if (value != oldValue) {
|
||||
if (oldValue instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) oldValue).valueUnbound(
|
||||
new HttpSessionBindingEvent(this, name, oldValue));
|
||||
((HttpSessionBindingListener) oldValue)
|
||||
.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
@@ -151,8 +147,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
}
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) value)
|
||||
.valueBound(new HttpSessionBindingEvent(this, name, value));
|
||||
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
@@ -173,8 +168,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
this.session.removeAttribute(name);
|
||||
if (oldValue instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) oldValue)
|
||||
.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
|
||||
((HttpSessionBindingListener) oldValue).valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
@@ -193,20 +187,19 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
this.invalidated = true;
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.old = !isNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
checkState();
|
||||
return !this.old;
|
||||
}
|
||||
|
||||
void markNotNew() {
|
||||
this.old = true;
|
||||
}
|
||||
|
||||
private void checkState() {
|
||||
if (this.invalidated) {
|
||||
throw new IllegalStateException(
|
||||
"The HttpSession has already be invalidated.");
|
||||
throw new IllegalStateException("The HttpSession has already be invalidated.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,8 +49,7 @@ public interface HttpSessionIdResolver {
|
||||
* @param response the current response
|
||||
* @param sessionId the session id
|
||||
*/
|
||||
void setSessionId(HttpServletRequest request, HttpServletResponse response,
|
||||
String sessionId);
|
||||
void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId);
|
||||
|
||||
/**
|
||||
* Instruct the client to end the current session. This method is invoked when a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,9 +25,6 @@ import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Base class for response wrappers which encapsulate the logic for handling an event when
|
||||
* the {@link javax.servlet.http.HttpServletResponse} is committed.
|
||||
@@ -36,7 +33,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean disableOnCommitted;
|
||||
|
||||
@@ -68,6 +64,12 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
super.addHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLengthLong(long len) {
|
||||
setContentLength(len);
|
||||
super.setContentLengthLong(len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLength(int len) {
|
||||
setContentLength((long) len);
|
||||
@@ -85,7 +87,7 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
* {@link javax.servlet.http.HttpServletResponse} is committed. This can be useful in
|
||||
* the event that Async Web Requests are made.
|
||||
*/
|
||||
public void disableOnResponseCommitted() {
|
||||
private void disableOnResponseCommitted() {
|
||||
this.disableOnCommitted = true;
|
||||
}
|
||||
|
||||
@@ -204,13 +206,11 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
/**
|
||||
* Adds the contentLengthToWrite to the total contentWritten size and checks to see if
|
||||
* the response should be written.
|
||||
*
|
||||
* @param contentLengthToWrite the size of the content that is about to be written.
|
||||
*/
|
||||
private void checkContentLength(long contentLengthToWrite) {
|
||||
this.contentWritten += contentLengthToWrite;
|
||||
boolean isBodyFullyWritten = this.contentLength > 0
|
||||
&& this.contentWritten >= this.contentLength;
|
||||
boolean isBodyFullyWritten = this.contentLength > 0 && this.contentWritten >= this.contentLength;
|
||||
int bufferSize = getBufferSize();
|
||||
boolean requiresFlush = bufferSize > 0 && this.contentWritten >= bufferSize;
|
||||
if (isBodyFullyWritten || requiresFlush) {
|
||||
@@ -234,9 +234,11 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
* calling the prior to methods that commit the response. We delegate all methods to
|
||||
* the original {@link java.io.PrintWriter} to ensure that the behavior is as close to
|
||||
* the original {@link java.io.PrintWriter} as possible. See SEC-2039
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private class SaveContextPrintWriter extends PrintWriter {
|
||||
|
||||
private final PrintWriter delegate;
|
||||
|
||||
SaveContextPrintWriter(PrintWriter delegate) {
|
||||
@@ -466,6 +468,7 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
trackContentLength(c);
|
||||
return this.delegate.append(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -477,6 +480,7 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private class SaveContextServletOutputStream extends ServletOutputStream {
|
||||
|
||||
private final ServletOutputStream delegate;
|
||||
|
||||
SaveContextServletOutputStream(ServletOutputStream delegate) {
|
||||
@@ -634,5 +638,7 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
this.delegate.setWriteListener(writeListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,19 +37,18 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class OncePerRequestFilter implements Filter {
|
||||
|
||||
/**
|
||||
* Suffix that gets appended to the filter name for the "already filtered" request
|
||||
* attribute.
|
||||
*/
|
||||
public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
|
||||
|
||||
private String alreadyFilteredAttributeName = getClass().getName()
|
||||
.concat(ALREADY_FILTERED_SUFFIX);
|
||||
private String alreadyFilteredAttributeName = getClass().getName().concat(ALREADY_FILTERED_SUFFIX);
|
||||
|
||||
/**
|
||||
* This {@code doFilter} implementation stores a request attribute for
|
||||
* "already filtered", proceeding without filtering again if the attribute is already
|
||||
* there.
|
||||
* This {@code doFilter} implementation stores a request attribute for "already
|
||||
* filtered", proceeding without filtering again if the attribute is already there.
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param filterChain the filter chain
|
||||
@@ -57,24 +56,22 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
* @throws IOException in case of I/O operation exception
|
||||
*/
|
||||
@Override
|
||||
public final void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (!(request instanceof HttpServletRequest)
|
||||
|| !(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException(
|
||||
"OncePerRequestFilter just supports HTTP requests");
|
||||
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
|
||||
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
|
||||
}
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
String alreadyFilteredAttributeName = this.alreadyFilteredAttributeName;
|
||||
alreadyFilteredAttributeName = updateForErrorDispatch(
|
||||
alreadyFilteredAttributeName, request);
|
||||
boolean hasAlreadyFilteredAttribute = request
|
||||
.getAttribute(alreadyFilteredAttributeName) != null;
|
||||
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
|
||||
|
||||
if (hasAlreadyFilteredAttribute) {
|
||||
|
||||
if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
|
||||
doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
|
||||
return;
|
||||
}
|
||||
// Proceed without invoking this filter...
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
@@ -91,15 +88,39 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
}
|
||||
}
|
||||
|
||||
private String updateForErrorDispatch(String alreadyFilteredAttributeName,
|
||||
ServletRequest request) {
|
||||
// Jetty does ERROR dispatch within sendError, so request attribute is still present
|
||||
// Use a separate attribute for ERROR dispatches
|
||||
if (DispatcherType.ERROR.equals(request.getDispatcherType())
|
||||
&& request.getAttribute(alreadyFilteredAttributeName) != null) {
|
||||
return alreadyFilteredAttributeName + ".ERROR";
|
||||
}
|
||||
return alreadyFilteredAttributeName;
|
||||
/**
|
||||
* Return the name of the request attribute that identifies that a request is already
|
||||
* filtered.
|
||||
* <p>
|
||||
* The default implementation takes the configured name of the concrete filter
|
||||
* instance and appends ".FILTERED". If the filter is not fully initialized, it falls
|
||||
* back to its class name.
|
||||
* @return the name of request attribute indicating already filtered request
|
||||
* @see #ALREADY_FILTERED_SUFFIX
|
||||
*/
|
||||
protected String getAlreadyFilteredAttributeName() {
|
||||
return this.alreadyFilteredAttributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically an ERROR dispatch happens after the REQUEST dispatch completes, and the
|
||||
* filter chain starts anew. On some servers however the ERROR dispatch may be nested
|
||||
* within the REQUEST dispatch, e.g. as a result of calling {@code sendError} on the
|
||||
* response. In that case we are still in the filter chain, on the same thread, but
|
||||
* the request and response have been switched to the original, unwrapped ones.
|
||||
* <p>
|
||||
* Sub-classes may use this method to filter such nested ERROR dispatches and re-apply
|
||||
* wrapping on the request or response. {@code ThreadLocal} context, if any, should
|
||||
* still be active as we are still nested within the filter chain.
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param filterChain the filter chain
|
||||
* @throws ServletException if request is not HTTP request
|
||||
* @throws IOException in case of I/O operation exception
|
||||
*/
|
||||
protected void doFilterNestedErrorDispatch(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
doFilter(request, response, filterChain);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +129,6 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
* <p>
|
||||
* Provides HttpServletRequest and HttpServletResponse arguments instead of the
|
||||
* default ServletRequest and ServletResponse ones.
|
||||
*
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @param filterChain the FilterChain
|
||||
@@ -116,9 +136,8 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
* @throws IOException thrown when an I/O exception of some sort has occurred
|
||||
* @see Filter#doFilter
|
||||
*/
|
||||
protected abstract void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException;
|
||||
protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
@@ -127,4 +146,5 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,6 +40,7 @@ import org.springframework.web.context.ServletContextAware;
|
||||
*/
|
||||
public class SessionEventHttpSessionListenerAdapter
|
||||
implements ApplicationListener<AbstractSessionEvent>, ServletContextAware {
|
||||
|
||||
private final List<HttpSessionListener> listeners;
|
||||
|
||||
private ServletContext context;
|
||||
@@ -75,8 +76,7 @@ public class SessionEventHttpSessionListenerAdapter
|
||||
|
||||
private HttpSessionEvent createHttpSessionEvent(AbstractSessionEvent event) {
|
||||
Session session = event.getSession();
|
||||
HttpSession httpSession = new HttpSessionAdapter<>(session,
|
||||
this.context);
|
||||
HttpSession httpSession = new HttpSessionAdapter<>(session, this.context);
|
||||
return new HttpSessionEvent(httpSession);
|
||||
}
|
||||
|
||||
@@ -91,4 +91,5 @@ public class SessionEventHttpSessionListenerAdapter
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.context = servletContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,25 +79,21 @@ import org.springframework.session.SessionRepository;
|
||||
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
|
||||
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
|
||||
|
||||
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class
|
||||
.getName().concat(".SESSION_LOGGER");
|
||||
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class.getName().concat(".SESSION_LOGGER");
|
||||
|
||||
private static final Log SESSION_LOGGER = LogFactory.getLog(SESSION_LOGGER_NAME);
|
||||
|
||||
/**
|
||||
* The session repository request attribute name.
|
||||
*/
|
||||
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class
|
||||
.getName();
|
||||
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class.getName();
|
||||
|
||||
/**
|
||||
* Invalid session id (not backed by the session repository) request attribute name.
|
||||
*/
|
||||
public static final String INVALID_SESSION_ID_ATTR = SESSION_REPOSITORY_ATTR
|
||||
+ ".invalidSessionId";
|
||||
public static final String INVALID_SESSION_ID_ATTR = SESSION_REPOSITORY_ATTR + ".invalidSessionId";
|
||||
|
||||
private static final String CURRENT_SESSION_ATTR = SESSION_REPOSITORY_ATTR
|
||||
+ ".CURRENT_SESSION";
|
||||
private static final String CURRENT_SESSION_ATTR = SESSION_REPOSITORY_ATTR + ".CURRENT_SESSION";
|
||||
|
||||
/**
|
||||
* The default filter order.
|
||||
@@ -110,7 +106,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param sessionRepository the <code>SessionRepository</code> to use. Cannot be null.
|
||||
*/
|
||||
public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
|
||||
@@ -123,7 +118,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
/**
|
||||
* Sets the {@link HttpSessionIdResolver} to be used. The default is a
|
||||
* {@link CookieHttpSessionIdResolver}.
|
||||
*
|
||||
* @param httpSessionIdResolver the {@link HttpSessionIdResolver} to use. Cannot be
|
||||
* null.
|
||||
*/
|
||||
@@ -135,15 +129,13 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
|
||||
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
|
||||
request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
|
||||
wrappedRequest, response);
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
|
||||
response);
|
||||
|
||||
try {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
@@ -153,14 +145,19 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterNestedErrorDispatch(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
doFilterInternal(request, response, filterChain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows ensuring that the session is saved if the response is committed.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class SessionRepositoryResponseWrapper
|
||||
extends OnCommittedResponseWrapper {
|
||||
private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {
|
||||
|
||||
private final SessionRepositoryRequestWrapper request;
|
||||
|
||||
@@ -169,8 +166,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
* @param request the request to be wrapped
|
||||
* @param response the response to be wrapped
|
||||
*/
|
||||
SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,
|
||||
HttpServletResponse response) {
|
||||
SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {
|
||||
super(response);
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("request cannot be null");
|
||||
@@ -182,6 +178,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
protected void onResponseCommitted() {
|
||||
this.request.commitSession();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,8 +189,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class SessionRepositoryRequestWrapper
|
||||
extends HttpServletRequestWrapper {
|
||||
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
@@ -207,8 +203,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private boolean requestedSessionInvalidated;
|
||||
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
|
||||
super(request);
|
||||
this.response = response;
|
||||
}
|
||||
@@ -221,8 +216,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
HttpSessionWrapper wrappedSession = getCurrentSession();
|
||||
if (wrappedSession == null) {
|
||||
if (isInvalidateClientSession()) {
|
||||
SessionRepositoryFilter.this.httpSessionIdResolver.expireSession(this,
|
||||
this.response);
|
||||
SessionRepositoryFilter.this.httpSessionIdResolver.expireSession(this, this.response);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -230,10 +224,8 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
clearRequestedSessionCache();
|
||||
SessionRepositoryFilter.this.sessionRepository.save(session);
|
||||
String sessionId = session.getId();
|
||||
if (!isRequestedSessionIdValid()
|
||||
|| !sessionId.equals(getRequestedSessionId())) {
|
||||
SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this,
|
||||
this.response, sessionId);
|
||||
if (!isRequestedSessionIdValid() || !sessionId.equals(getRequestedSessionId())) {
|
||||
SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +292,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
requestedSession.setLastAccessedTime(Instant.now());
|
||||
this.requestedSessionIdValid = true;
|
||||
currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
|
||||
currentSession.setNew(false);
|
||||
currentSession.markNotNew();
|
||||
setCurrentSession(currentSession);
|
||||
return currentSession;
|
||||
}
|
||||
@@ -317,12 +309,15 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
if (!create) {
|
||||
return null;
|
||||
}
|
||||
if (SessionRepositoryFilter.this.httpSessionIdResolver instanceof CookieHttpSessionIdResolver
|
||||
&& this.response.isCommitted()) {
|
||||
throw new IllegalStateException("Cannot create a session after the response has been committed");
|
||||
}
|
||||
if (SESSION_LOGGER.isDebugEnabled()) {
|
||||
SESSION_LOGGER.debug(
|
||||
"A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
|
||||
+ SESSION_LOGGER_NAME,
|
||||
new RuntimeException(
|
||||
"For debugging purposes only (not an error)"));
|
||||
new RuntimeException("For debugging purposes only (not an error)"));
|
||||
}
|
||||
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
@@ -352,14 +347,12 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private S getRequestedSession() {
|
||||
if (!this.requestedSessionCached) {
|
||||
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
|
||||
.resolveSessionIds(this);
|
||||
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);
|
||||
S session = SessionRepositoryFilter.this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
this.requestedSession = session;
|
||||
this.requestedSessionId = sessionId;
|
||||
@@ -397,6 +390,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
clearRequestedSessionCache();
|
||||
SessionRepositoryFilter.this.sessionRepository.deleteById(getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,8 +398,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
*
|
||||
* @since 1.3.4
|
||||
*/
|
||||
private final class SessionCommittingRequestDispatcher
|
||||
implements RequestDispatcher {
|
||||
private final class SessionCommittingRequestDispatcher implements RequestDispatcher {
|
||||
|
||||
private final RequestDispatcher delegate;
|
||||
|
||||
@@ -414,14 +407,12 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forward(ServletRequest request, ServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
this.delegate.forward(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void include(ServletRequest request, ServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
SessionRepositoryRequestWrapper.this.commitSession();
|
||||
this.delegate.include(request, response);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -61,12 +61,14 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link Clock} to use to set lastAccessTime on every created
|
||||
* session and to calculate if it is expired.
|
||||
* <p>This may be useful to align to different timezone or to set the clock
|
||||
* back in a test, e.g. {@code Clock.offset(clock, Duration.ofMinutes(-31))}
|
||||
* in order to simulate session expiration.
|
||||
* <p>By default this is {@code Clock.system(ZoneId.of("GMT"))}.
|
||||
* Configure the {@link Clock} to use to set lastAccessTime on every created session
|
||||
* and to calculate if it is expired.
|
||||
* <p>
|
||||
* This may be useful to align to different timezone or to set the clock back in a
|
||||
* test, e.g. {@code Clock.offset(clock, Duration.ofMinutes(-31))} in order to
|
||||
* simulate session expiration.
|
||||
* <p>
|
||||
* By default this is {@code Clock.system(ZoneId.of("GMT"))}.
|
||||
* @param clock the clock to use
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
@@ -90,8 +92,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
@Override
|
||||
public Mono<WebSession> retrieveSession(String sessionId) {
|
||||
return this.sessions.findById(sessionId)
|
||||
.doOnNext((session) -> session.setLastAccessedTime(this.clock.instant()))
|
||||
.map(this::existingSession);
|
||||
.doOnNext((session) -> session.setLastAccessedTime(this.clock.instant())).map(this::existingSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,8 +134,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
@Override
|
||||
public Mono<Void> changeSessionId() {
|
||||
return Mono.defer(() -> {
|
||||
this.session
|
||||
.changeSessionId();
|
||||
this.session.changeSessionId();
|
||||
return save();
|
||||
});
|
||||
}
|
||||
@@ -152,8 +152,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
State value = this.state.get();
|
||||
return (State.STARTED.equals(value)
|
||||
|| (State.NEW.equals(value) && !getAttributes().isEmpty()));
|
||||
return (State.STARTED.equals(value) || (State.NEW.equals(value) && !getAttributes().isEmpty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,10 +197,13 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
public void setMaxIdleTime(Duration maxIdleTime) {
|
||||
this.session.setMaxInactiveInterval(maxIdleTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum State {
|
||||
|
||||
NEW, STARTED, EXPIRED
|
||||
|
||||
}
|
||||
|
||||
private static class SpringSessionMap implements Map<String, Object> {
|
||||
@@ -226,8 +228,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return key instanceof String
|
||||
&& this.session.getAttributeNames().contains(key);
|
||||
return key instanceof String && this.session.getAttributeNames().contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,5 +347,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -93,8 +93,7 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
|
||||
public final void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
if (registry instanceof WebMvcStompEndpointRegistry) {
|
||||
WebMvcStompEndpointRegistry mvcRegistry = (WebMvcStompEndpointRegistry) registry;
|
||||
configureStompEndpoints(new SessionStompEndpointRegistry(mvcRegistry,
|
||||
sessionRepositoryInterceptor()));
|
||||
configureStompEndpoints(new SessionStompEndpointRegistry(mvcRegistry, sessionRepositoryInterceptor()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +101,6 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
|
||||
* Register STOMP endpoints mapping each to a specific URL and (optionally) enabling
|
||||
* and configuring SockJS fallback options with a
|
||||
* {@link SessionRepositoryMessageInterceptor} automatically added as an interceptor.
|
||||
*
|
||||
* @param registry the {@link StompEndpointRegistry} which automatically has a
|
||||
* {@link SessionRepositoryMessageInterceptor} added to it.
|
||||
*/
|
||||
@@ -133,19 +131,19 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
|
||||
* A {@link StompEndpointRegistry} that applies {@link HandshakeInterceptor}.
|
||||
*/
|
||||
static class SessionStompEndpointRegistry implements StompEndpointRegistry {
|
||||
|
||||
private final WebMvcStompEndpointRegistry registry;
|
||||
|
||||
private final HandshakeInterceptor interceptor;
|
||||
|
||||
SessionStompEndpointRegistry(WebMvcStompEndpointRegistry registry,
|
||||
HandshakeInterceptor interceptor) {
|
||||
SessionStompEndpointRegistry(WebMvcStompEndpointRegistry registry, HandshakeInterceptor interceptor) {
|
||||
this.registry = registry;
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompWebSocketEndpointRegistration addEndpoint(String... paths) {
|
||||
StompWebSocketEndpointRegistration endpoints = this.registry
|
||||
.addEndpoint(paths);
|
||||
StompWebSocketEndpointRegistration endpoints = this.registry.addEndpoint(paths);
|
||||
endpoints.addInterceptors(this.interceptor);
|
||||
return endpoints;
|
||||
}
|
||||
@@ -161,9 +159,10 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebMvcStompEndpointRegistry setErrorHandler(
|
||||
StompSubProtocolErrorHandler errorHandler) {
|
||||
public WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler errorHandler) {
|
||||
return this.registry.setErrorHandler(errorHandler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,4 +44,5 @@ public class SessionConnectEvent extends ApplicationEvent {
|
||||
public WebSocketSession getWebSocketSession() {
|
||||
return this.webSocketSession;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,24 +38,19 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*
|
||||
* @see WebSocketRegistryListener
|
||||
*/
|
||||
public final class WebSocketConnectHandlerDecoratorFactory
|
||||
implements WebSocketHandlerDecoratorFactory {
|
||||
public final class WebSocketConnectHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(WebSocketConnectHandlerDecoratorFactory.class);
|
||||
private static final Log logger = LogFactory.getLog(WebSocketConnectHandlerDecoratorFactory.class);
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param eventPublisher the {@link ApplicationEventPublisher} to use. Cannot be null.
|
||||
*/
|
||||
public WebSocketConnectHandlerDecoratorFactory(
|
||||
ApplicationEventPublisher eventPublisher) {
|
||||
public WebSocketConnectHandlerDecoratorFactory(ApplicationEventPublisher eventPublisher) {
|
||||
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
@@ -72,8 +67,7 @@ public final class WebSocketConnectHandlerDecoratorFactory
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession wsSession)
|
||||
throws Exception {
|
||||
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception {
|
||||
super.afterConnectionEstablished(wsSession);
|
||||
|
||||
publishEvent(new SessionConnectEvent(this, wsSession));
|
||||
@@ -81,12 +75,13 @@ public final class WebSocketConnectHandlerDecoratorFactory
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
try {
|
||||
WebSocketConnectHandlerDecoratorFactory.this.eventPublisher
|
||||
.publishEvent(event);
|
||||
WebSocketConnectHandlerDecoratorFactory.this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("Error publishing " + event + ".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,18 +41,15 @@ import org.springframework.web.socket.messaging.SessionDisconnectEvent;
|
||||
* {@link WebSocketSession} is closed.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Mark Anderson
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class WebSocketRegistryListener
|
||||
implements ApplicationListener<ApplicationEvent> {
|
||||
public final class WebSocketRegistryListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebSocketRegistryListener.class);
|
||||
|
||||
static final CloseStatus SESSION_EXPIRED_STATUS = new CloseStatus(
|
||||
CloseStatus.POLICY_VIOLATION.getCode(),
|
||||
static final CloseStatus SESSION_EXPIRED_STATUS = new CloseStatus(CloseStatus.POLICY_VIOLATION.getCode(),
|
||||
"This connection was established under an authenticated HTTP Session that has expired");
|
||||
|
||||
private final ConcurrentHashMap<String, Map<String, WebSocketSession>> httpSessionIdToWsSessions = new ConcurrentHashMap<>();
|
||||
@@ -72,8 +69,7 @@ public final class WebSocketRegistryListener
|
||||
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor
|
||||
.getSessionAttributes(e.getMessage().getHeaders());
|
||||
String httpSessionId = (sessionAttributes != null)
|
||||
? SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes)
|
||||
: null;
|
||||
? SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes) : null;
|
||||
afterConnectionClosed(httpSessionId, e.getSessionId());
|
||||
}
|
||||
}
|
||||
@@ -98,8 +94,7 @@ public final class WebSocketRegistryListener
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions
|
||||
.get(httpSessionId);
|
||||
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions.get(httpSessionId);
|
||||
if (sessions != null) {
|
||||
boolean result = sessions.remove(wsSessionId) != null;
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -108,16 +103,15 @@ public final class WebSocketRegistryListener
|
||||
if (sessions.isEmpty()) {
|
||||
this.httpSessionIdToWsSessions.remove(httpSessionId);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removed the corresponding HTTP Session for "
|
||||
+ wsSessionId + " since it contained no WebSocket mappings");
|
||||
logger.debug("Removed the corresponding HTTP Session for " + wsSessionId
|
||||
+ " since it contained no WebSocket mappings");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWsSession(String httpSessionId, WebSocketSession wsSession) {
|
||||
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions
|
||||
.get(httpSessionId);
|
||||
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions.get(httpSessionId);
|
||||
if (sessions == null) {
|
||||
sessions = new ConcurrentHashMap<>();
|
||||
this.httpSessionIdToWsSessions.putIfAbsent(httpSessionId, sessions);
|
||||
@@ -127,25 +121,22 @@ public final class WebSocketRegistryListener
|
||||
}
|
||||
|
||||
private void closeWsSessions(String httpSessionId) {
|
||||
Map<String, WebSocketSession> sessionsToClose = this.httpSessionIdToWsSessions
|
||||
.remove(httpSessionId);
|
||||
Map<String, WebSocketSession> sessionsToClose = this.httpSessionIdToWsSessions.remove(httpSessionId);
|
||||
if (sessionsToClose == null) {
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Closing WebSocket connections associated to expired HTTP Session "
|
||||
+ httpSessionId);
|
||||
logger.debug("Closing WebSocket connections associated to expired HTTP Session " + httpSessionId);
|
||||
}
|
||||
for (WebSocketSession toClose : sessionsToClose.values()) {
|
||||
try {
|
||||
toClose.close(SESSION_EXPIRED_STATUS);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug(
|
||||
"Failed to close WebSocketSession (this is nothing to worry about but for debugging only)",
|
||||
logger.debug("Failed to close WebSocketSession (this is nothing to worry about but for debugging only)",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -72,15 +72,13 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param sessionRepository the {@link SessionRepository} to use. Cannot be null.
|
||||
*/
|
||||
public SessionRepositoryMessageInterceptor(SessionRepository<S> sessionRepository) {
|
||||
Assert.notNull(sessionRepository, "sessionRepository cannot be null");
|
||||
this.sessionRepository = sessionRepository;
|
||||
this.matchingMessageTypes = EnumSet.of(SimpMessageType.CONNECT,
|
||||
SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE,
|
||||
SimpMessageType.UNSUBSCRIBE);
|
||||
this.matchingMessageTypes = EnumSet.of(SimpMessageType.CONNECT, SimpMessageType.MESSAGE,
|
||||
SimpMessageType.SUBSCRIBE, SimpMessageType.UNSUBSCRIBE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,14 +92,12 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
* The default is: SimpMessageType.CONNECT, SimpMessageType.MESSAGE,
|
||||
* SimpMessageType.SUBSCRIBE, SimpMessageType.UNSUBSCRIBE.
|
||||
* </p>
|
||||
*
|
||||
* @param matchingMessageTypes the {@link SimpMessageType} to match on in
|
||||
* {@link #preSend(Message, MessageChannel)}, else the {@link Message} is continued
|
||||
* without accessing or updating the {@link Session}
|
||||
*/
|
||||
public void setMatchingMessageTypes(Set<SimpMessageType> matchingMessageTypes) {
|
||||
Assert.notEmpty(matchingMessageTypes,
|
||||
"matchingMessageTypes cannot be null or empty");
|
||||
Assert.notEmpty(matchingMessageTypes, "matchingMessageTypes cannot be null or empty");
|
||||
this.matchingMessageTypes = matchingMessageTypes;
|
||||
}
|
||||
|
||||
@@ -110,16 +106,12 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
if (message == null) {
|
||||
return message;
|
||||
}
|
||||
SimpMessageType messageType = SimpMessageHeaderAccessor
|
||||
.getMessageType(message.getHeaders());
|
||||
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(message.getHeaders());
|
||||
if (!this.matchingMessageTypes.contains(messageType)) {
|
||||
return message;
|
||||
}
|
||||
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor
|
||||
.getSessionAttributes(message.getHeaders());
|
||||
String sessionId = (sessionHeaders != null)
|
||||
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME)
|
||||
: null;
|
||||
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor.getSessionAttributes(message.getHeaders());
|
||||
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) {
|
||||
@@ -132,8 +124,8 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
|
||||
Map<String, Object> attributes) {
|
||||
if (request instanceof ServletServerHttpRequest) {
|
||||
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
|
||||
HttpSession session = servletRequest.getServletRequest().getSession(false);
|
||||
@@ -145,8 +137,8 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler, Exception exception) {
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
|
||||
Exception exception) {
|
||||
}
|
||||
|
||||
public static String getSessionId(Map<String, Object> attributes) {
|
||||
@@ -156,4 +148,5 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
public static void setSessionId(Map<String, Object> attributes, String sessionId) {
|
||||
attributes.put(SPRING_SESSION_ID_ATTR_NAME, sessionId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DelegatingIndexResolver}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
class DelegatingIndexResolverTests {
|
||||
|
||||
private DelegatingIndexResolver<MapSession> indexResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.indexResolver = new DelegatingIndexResolver<>(new TestIndexResolver("one"), new TestIndexResolver("two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolve() {
|
||||
MapSession session = new MapSession();
|
||||
session.setAttribute("one", "first");
|
||||
session.setAttribute("two", "second");
|
||||
Map<String, String> indexes = this.indexResolver.resolveIndexesFor(session);
|
||||
assertThat(indexes).hasSize(2);
|
||||
assertThat(indexes.get("one")).isEqualTo("first");
|
||||
assertThat(indexes.get("two")).isEqualTo("second");
|
||||
}
|
||||
|
||||
private static class TestIndexResolver implements IndexResolver<MapSession> {
|
||||
|
||||
private final String supportedIndex;
|
||||
|
||||
TestIndexResolver(String supportedIndex) {
|
||||
this.supportedIndex = supportedIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> resolveIndexesFor(MapSession session) {
|
||||
return Collections.singletonMap(this.supportedIndex, session.getAttribute(this.supportedIndex));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,20 +29,20 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Tests for {@link MapSessionRepository}.
|
||||
*/
|
||||
public class MapSessionRepositoryTests {
|
||||
class MapSessionRepositoryTests {
|
||||
|
||||
private MapSessionRepository repository;
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.repository = new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
this.session = new MapSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionExpired() {
|
||||
void getSessionExpired() {
|
||||
this.session.setMaxInactiveInterval(Duration.ofSeconds(1));
|
||||
this.session.setLastAccessedTime(Instant.now().minus(5, ChronoUnit.MINUTES));
|
||||
this.repository.save(this.session);
|
||||
@@ -51,29 +51,25 @@ public class MapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionDefaultExpiration() {
|
||||
void createSessionDefaultExpiration() {
|
||||
Session session = this.repository.createSession();
|
||||
|
||||
assertThat(session).isInstanceOf(MapSession.class);
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionCustomDefaultExpiration() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(
|
||||
(int) expectedMaxInterval.getSeconds());
|
||||
void createSessionCustomDefaultExpiration() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expectedMaxInterval);
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInterval);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenNotYetSaved() {
|
||||
void changeSessionIdWhenNotYetSaved() {
|
||||
MapSession createSession = this.repository.createSession();
|
||||
|
||||
String originalId = createSession.getId();
|
||||
@@ -86,7 +82,7 @@ public class MapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSaved() {
|
||||
void changeSessionIdWhenSaved() {
|
||||
MapSession createSession = this.repository.createSession();
|
||||
|
||||
this.repository.save(createSession);
|
||||
@@ -101,7 +97,7 @@ public class MapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test // gh-1120
|
||||
public void getAttributeNamesAndRemove() {
|
||||
void getAttributeNamesAndRemove() {
|
||||
MapSession session = this.repository.createSession();
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -26,38 +26,37 @@ import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
public class MapSessionTests {
|
||||
class MapSessionTests {
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.session = new MapSession();
|
||||
this.session.setLastAccessedTime(Instant.ofEpochMilli(1413258262962L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNullSession() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new MapSession((Session) null))
|
||||
void constructorNullSession() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new MapSession((Session) null))
|
||||
.withMessage("session cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeWhenNullThenNull() {
|
||||
void getAttributeWhenNullThenNull() {
|
||||
String result = this.session.getAttribute("attrName");
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOrDefaultWhenNullThenDefaultValue() {
|
||||
void getAttributeOrDefaultWhenNullThenDefaultValue() {
|
||||
String defaultValue = "default";
|
||||
String result = this.session.getAttributeOrDefault("attrName", defaultValue);
|
||||
assertThat(result).isEqualTo(defaultValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOrDefaultWhenNotNullThenDefaultValue() {
|
||||
void getAttributeOrDefaultWhenNotNullThenDefaultValue() {
|
||||
String defaultValue = "default";
|
||||
String attrValue = "value";
|
||||
String attrName = "attrName";
|
||||
@@ -69,14 +68,13 @@ public class MapSessionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequiredAttributeWhenNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.session.getRequiredAttribute("attrName"))
|
||||
void getRequiredAttributeWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.session.getRequiredAttribute("attrName"))
|
||||
.withMessage("Required attribute 'attrName' is missing.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequiredAttributeWhenNotNullThenReturns() {
|
||||
void getRequiredAttributeWhenNotNullThenReturns() {
|
||||
String attrValue = "value";
|
||||
String attrName = "attrName";
|
||||
this.session.setAttribute(attrName, attrValue);
|
||||
@@ -90,7 +88,7 @@ public class MapSessionTests {
|
||||
* Ensure conforms to the javadoc of {@link Session}
|
||||
*/
|
||||
@Test
|
||||
public void setAttributeNullObjectRemoves() {
|
||||
void setAttributeNullObjectRemoves() {
|
||||
String attr = "attr";
|
||||
this.session.setAttribute(attr, new Object());
|
||||
this.session.setAttribute(attr, null);
|
||||
@@ -98,43 +96,43 @@ public class MapSessionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsNonSessionFalse() {
|
||||
void equalsNonSessionFalse() {
|
||||
assertThat(this.session.equals(new Object())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsCustomSession() {
|
||||
void equalsCustomSession() {
|
||||
CustomSession other = new CustomSession();
|
||||
this.session.setId(other.getId());
|
||||
assertThat(this.session.equals(other)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeEqualsIdHashCode() {
|
||||
void hashCodeEqualsIdHashCode() {
|
||||
this.session.setId("constantId");
|
||||
assertThat(this.session.hashCode()).isEqualTo(this.session.getId().hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExpiredExact() {
|
||||
void isExpiredExact() {
|
||||
Instant now = Instant.ofEpochMilli(1413260062962L);
|
||||
assertThat(this.session.isExpired(now)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExpiredOneMsTooSoon() {
|
||||
void isExpiredOneMsTooSoon() {
|
||||
Instant now = Instant.ofEpochMilli(1413260062961L);
|
||||
assertThat(this.session.isExpired(now)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExpiredOneMsAfter() {
|
||||
void isExpiredOneMsAfter() {
|
||||
Instant now = Instant.ofEpochMilli(1413260062963L);
|
||||
assertThat(this.session.isExpired(now)).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-1120
|
||||
public void getAttributeNamesAndRemove() {
|
||||
void getAttributeNamesAndRemove() {
|
||||
this.session.setAttribute("attribute1", "value1");
|
||||
this.session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -206,6 +204,7 @@ public class MapSessionTests {
|
||||
public boolean isExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link PrincipalNameIndexResolver}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
class PrincipalNameIndexResolverTests {
|
||||
|
||||
private static final String PRINCIPAL_NAME = "principalName";
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private PrincipalNameIndexResolver<Session> indexResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.indexResolver = new PrincipalNameIndexResolver<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveFromPrincipalName() {
|
||||
MapSession session = new MapSession();
|
||||
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, PRINCIPAL_NAME);
|
||||
assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveFromSpringSecurityContext() {
|
||||
Authentication authentication = new UsernamePasswordAuthenticationToken(PRINCIPAL_NAME, "notused",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
context.setAuthentication(authentication);
|
||||
MapSession session = new MapSession();
|
||||
session.setAttribute(SPRING_SECURITY_CONTEXT, context);
|
||||
assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,20 +35,20 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ReactiveMapSessionRepositoryTests {
|
||||
class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
private ReactiveMapSessionRepository repository;
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.repository = new ReactiveMapSessionRepository(new HashMap<>());
|
||||
this.session = new MapSession("session-id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMapThenFound() {
|
||||
void constructorMapThenFound() {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(this.session.getId(), this.session);
|
||||
this.repository = new ReactiveMapSessionRepository(sessions);
|
||||
@@ -59,21 +59,20 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
.withMessage("sessions cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenNoSubscribersThenNotFound() {
|
||||
void saveWhenNoSubscribersThenNotFound() {
|
||||
this.repository.save(this.session);
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenSubscriberThenFound() {
|
||||
void saveWhenSubscriberThenFound() {
|
||||
this.repository.save(this.session).block();
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
@@ -82,7 +81,7 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenExpiredRemovesFromSessionMap() {
|
||||
void findByIdWhenExpiredRemovesFromSessionMap() {
|
||||
this.session.setMaxInactiveInterval(Duration.ofMinutes(1));
|
||||
this.session.setLastAccessedTime(Instant.now().minus(5, ChronoUnit.MINUTES));
|
||||
|
||||
@@ -95,20 +94,17 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenDefaultMaxInactiveIntervalThenDefaultMaxInactiveInterval() {
|
||||
void createSessionWhenDefaultMaxInactiveIntervalThenDefaultMaxInactiveInterval() {
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session).isInstanceOf(MapSession.class);
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository
|
||||
.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
@@ -116,7 +112,7 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenNotYetSaved() {
|
||||
void changeSessionIdWhenNotYetSaved() {
|
||||
MapSession createSession = this.repository.createSession().block();
|
||||
|
||||
String originalId = createSession.getId();
|
||||
@@ -129,7 +125,7 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSaved() {
|
||||
void changeSessionIdWhenSaved() {
|
||||
MapSession createSession = this.repository.createSession().block();
|
||||
|
||||
this.repository.save(createSession).block();
|
||||
@@ -144,7 +140,7 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test // gh-1120
|
||||
public void getAttributeNamesAndRemove() {
|
||||
void getAttributeNamesAndRemove() {
|
||||
MapSession session = this.repository.createSession().block();
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -61,7 +61,7 @@ import static org.mockito.Mockito.verify;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
|
||||
@Autowired
|
||||
private MockHttpServletRequest request;
|
||||
@@ -81,7 +81,7 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
private CookieSerializer cookieSerializer;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.chain = new MockFilterChain();
|
||||
|
||||
reset(this.sessionRepository);
|
||||
@@ -89,12 +89,11 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesReadSessionIds() throws Exception {
|
||||
void usesReadSessionIds() throws Exception {
|
||||
String sessionId = "sessionId";
|
||||
given(this.cookieSerializer.readCookieValues(any(HttpServletRequest.class)))
|
||||
.willReturn(Collections.singletonList(sessionId));
|
||||
given(this.sessionRepository.findById(anyString()))
|
||||
.willReturn(new MapSession(sessionId));
|
||||
given(this.sessionRepository.findById(anyString())).willReturn(new MapSession(sessionId));
|
||||
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
@@ -102,19 +101,18 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesWrite() throws Exception {
|
||||
void usesWrite() throws Exception {
|
||||
given(this.sessionRepository.createSession()).willReturn(new MapSession());
|
||||
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response,
|
||||
new MockFilterChain() {
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response, new MockFilterChain() {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
((HttpServletRequest) request).getSession();
|
||||
super.doFilter(request, response);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
((HttpServletRequest) request).getSession();
|
||||
super.doFilter(request, response);
|
||||
}
|
||||
});
|
||||
|
||||
verify(this.cookieSerializer).writeCookieValue(any(CookieValue.class));
|
||||
}
|
||||
@@ -128,12 +126,12 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public SessionRepository sessionRepository() {
|
||||
SessionRepository sessionRepository() {
|
||||
return mock(SessionRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
CookieSerializer cookieSerializer() {
|
||||
return mock(CookieSerializer.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,12 +45,12 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class SpringHttpSessionConfigurationTests {
|
||||
class SpringHttpSessionConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@AfterEach
|
||||
public void closeContext() {
|
||||
void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
@@ -62,28 +62,26 @@ public class SpringHttpSessionConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noSessionRepositoryConfiguration() {
|
||||
void noSessionRepositoryConfiguration() {
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> registerAndRefresh(EmptyConfiguration.class))
|
||||
.withMessageContaining("org.springframework.session.SessionRepository");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConfiguration() {
|
||||
void defaultConfiguration() {
|
||||
registerAndRefresh(DefaultConfiguration.class);
|
||||
|
||||
assertThat(this.context.getBean(SessionEventHttpSessionListenerAdapter.class))
|
||||
.isNotNull();
|
||||
assertThat(this.context.getBean(SessionEventHttpSessionListenerAdapter.class)).isNotNull();
|
||||
assertThat(this.context.getBean(SessionRepositoryFilter.class)).isNotNull();
|
||||
assertThat(this.context.getBean(SessionRepository.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionCookieConfigConfiguration() {
|
||||
void sessionCookieConfigConfiguration() {
|
||||
registerAndRefresh(SessionCookieConfigConfiguration.class);
|
||||
|
||||
SessionRepositoryFilter sessionRepositoryFilter = this.context
|
||||
.getBean(SessionRepositoryFilter.class);
|
||||
SessionRepositoryFilter sessionRepositoryFilter = this.context.getBean(SessionRepositoryFilter.class);
|
||||
assertThat(sessionRepositoryFilter).isNotNull();
|
||||
CookieHttpSessionIdResolver httpSessionIdResolver = (CookieHttpSessionIdResolver) ReflectionTestUtils
|
||||
.getField(sessionRepositoryFilter, "httpSessionIdResolver");
|
||||
@@ -91,22 +89,17 @@ public class SpringHttpSessionConfigurationTests {
|
||||
DefaultCookieSerializer cookieSerializer = (DefaultCookieSerializer) ReflectionTestUtils
|
||||
.getField(httpSessionIdResolver, "cookieSerializer");
|
||||
assertThat(cookieSerializer).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookieName"))
|
||||
.isEqualTo("test-name");
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookiePath"))
|
||||
.isEqualTo("test-path");
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookieMaxAge"))
|
||||
.isEqualTo(600);
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "domainName"))
|
||||
.isEqualTo("test-domain");
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookieName")).isEqualTo("test-name");
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookiePath")).isEqualTo("test-path");
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "cookieMaxAge")).isEqualTo(600);
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "domainName")).isEqualTo("test-domain");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rememberMeServicesConfiguration() {
|
||||
void rememberMeServicesConfiguration() {
|
||||
registerAndRefresh(RememberMeServicesConfiguration.class);
|
||||
|
||||
SessionRepositoryFilter sessionRepositoryFilter = this.context
|
||||
.getBean(SessionRepositoryFilter.class);
|
||||
SessionRepositoryFilter sessionRepositoryFilter = this.context.getBean(SessionRepositoryFilter.class);
|
||||
assertThat(sessionRepositoryFilter).isNotNull();
|
||||
CookieHttpSessionIdResolver httpSessionIdResolver = (CookieHttpSessionIdResolver) ReflectionTestUtils
|
||||
.getField(sessionRepositoryFilter, "httpSessionIdResolver");
|
||||
@@ -114,20 +107,20 @@ public class SpringHttpSessionConfigurationTests {
|
||||
DefaultCookieSerializer cookieSerializer = (DefaultCookieSerializer) ReflectionTestUtils
|
||||
.getField(httpSessionIdResolver, "cookieSerializer");
|
||||
assertThat(cookieSerializer).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer,
|
||||
"rememberMeRequestAttribute")).isEqualTo(
|
||||
SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
|
||||
assertThat(ReflectionTestUtils.getField(cookieSerializer, "rememberMeRequestAttribute"))
|
||||
.isEqualTo(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSpringHttpSession
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public MapSessionRepository sessionRepository() {
|
||||
MapSessionRepository sessionRepository() {
|
||||
return new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@@ -136,6 +129,7 @@ public class SpringHttpSessionConfigurationTests {
|
||||
@Configuration
|
||||
@EnableSpringHttpSession
|
||||
static class DefaultConfiguration extends BaseConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -143,7 +137,7 @@ public class SpringHttpSessionConfigurationTests {
|
||||
static class SessionCookieConfigConfiguration extends BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServletContext servletContext() {
|
||||
ServletContext servletContext() {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
servletContext.getSessionCookieConfig().setName("test-name");
|
||||
servletContext.getSessionCookieConfig().setDomain("test-domain");
|
||||
@@ -159,7 +153,7 @@ public class SpringHttpSessionConfigurationTests {
|
||||
static class RememberMeServicesConfiguration extends BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpringSessionRememberMeServices rememberMeServices() {
|
||||
SpringSessionRememberMeServices rememberMeServices() {
|
||||
return new SpringSessionRememberMeServices();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,18 +41,19 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public class SpringWebSessionConfigurationTests {
|
||||
class SpringWebSessionConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
void cleanup() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableSpringWebSessionConfiguresThings() {
|
||||
void enableSpringWebSessionConfiguresThings() {
|
||||
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(GoodConfig.class);
|
||||
@@ -69,19 +70,18 @@ public class SpringWebSessionConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingReactiveSessionRepositoryBreaksAppContext() {
|
||||
void missingReactiveSessionRepositoryBreaksAppContext() {
|
||||
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(BadConfig.class);
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(this.context::refresh)
|
||||
.withMessageContaining("Error creating bean with name 'webSessionManager'")
|
||||
.withMessageContaining("No qualifying bean of type '" + ReactiveSessionRepository.class.getCanonicalName());
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(this.context::refresh)
|
||||
.withMessageContaining("Error creating bean with name 'webSessionManager'").withMessageContaining(
|
||||
"No qualifying bean of type '" + ReactiveSessionRepository.class.getCanonicalName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultSessionIdResolverShouldBeCookieBased() {
|
||||
void defaultSessionIdResolverShouldBeCookieBased() {
|
||||
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(GoodConfig.class);
|
||||
@@ -92,7 +92,7 @@ public class SpringWebSessionConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void providedSessionIdResolverShouldBePickedUpAutomatically() {
|
||||
void providedSessionIdResolverShouldBePickedUpAutomatically() {
|
||||
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(OverrideSessionIdResolver.class);
|
||||
@@ -109,12 +109,14 @@ public class SpringWebSessionConfigurationTests {
|
||||
static class GoodConfig {
|
||||
|
||||
/**
|
||||
* Use Reactor-friendly, {@link java.util.Map}-backed {@link ReactiveSessionRepository} for test purposes.
|
||||
* Use Reactor-friendly, {@link java.util.Map}-backed
|
||||
* {@link ReactiveSessionRepository} for test purposes.
|
||||
*/
|
||||
@Bean
|
||||
ReactiveSessionRepository<?> reactiveSessionRepository() {
|
||||
return new ReactiveMapSessionRepository(new HashMap<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +139,7 @@ public class SpringWebSessionConfigurationTests {
|
||||
WebSessionIdResolver alternateWebSessionIdResolver() {
|
||||
return new HeaderWebSessionIdResolver();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
@@ -30,6 +31,7 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.core.AuthenticatedPrincipal;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
@@ -46,7 +48,7 @@ import static org.mockito.BDDMockito.when;
|
||||
/**
|
||||
* Tests for {@link SpringSessionBackedSessionRegistry}.
|
||||
*/
|
||||
public class SpringSessionBackedSessionRegistryTest {
|
||||
class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
private static final String SESSION_ID = "sessionId";
|
||||
|
||||
@@ -54,8 +56,7 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
private static final String USER_NAME = "userName";
|
||||
|
||||
private static final User PRINCIPAL = new User(USER_NAME, "password",
|
||||
Collections.emptyList());
|
||||
private static final User PRINCIPAL = new User(USER_NAME, "password", Collections.emptyList());
|
||||
|
||||
private static final Instant NOW = Instant.now();
|
||||
|
||||
@@ -71,74 +72,77 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionInformationForExistingSession() {
|
||||
void sessionInformationForExistingSession() {
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
SessionInformation sessionInfo = this.sessionRegistry.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().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionInformationForExpiredSession() {
|
||||
void sessionInformationForExpiredSession() {
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
session.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR,
|
||||
Boolean.TRUE);
|
||||
session.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, Boolean.TRUE);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
SessionInformation sessionInfo = this.sessionRegistry.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
assertThat(sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noSessionInformationForMissingSession() {
|
||||
assertThat(this.sessionRegistry.getSessionInformation("nonExistingSessionId"))
|
||||
.isNull();
|
||||
void noSessionInformationForMissingSession() {
|
||||
assertThat(this.sessionRegistry.getSessionInformation("nonExistingSessionId")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllSessions() {
|
||||
void getAllSessionsForUserDetails() {
|
||||
setUpSessions();
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry.getAllSessions(PRINCIPAL, true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllSessionsForAuthenticatedPrincipal() {
|
||||
setUpSessions();
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry
|
||||
.getAllSessions(PRINCIPAL, true);
|
||||
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID,
|
||||
SESSION_ID2);
|
||||
.getAllSessions((AuthenticatedPrincipal) () -> USER_NAME, true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNonExpiredSessions() {
|
||||
void getAllSessionsForPrincipal() {
|
||||
setUpSessions();
|
||||
List<SessionInformation> allSessionInfos = this.sessionRegistry.getAllSessions(new TestPrincipal(USER_NAME),
|
||||
true);
|
||||
assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNonExpiredSessions() {
|
||||
setUpSessions();
|
||||
|
||||
List<SessionInformation> nonExpiredSessionInfos = this.sessionRegistry
|
||||
.getAllSessions(PRINCIPAL, false);
|
||||
List<SessionInformation> nonExpiredSessionInfos = this.sessionRegistry.getAllSessions(PRINCIPAL, false);
|
||||
|
||||
assertThat(nonExpiredSessionInfos).extracting("sessionId")
|
||||
.containsExactly(SESSION_ID2);
|
||||
assertThat(nonExpiredSessionInfos).extracting("sessionId").containsExactly(SESSION_ID2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expireNow() {
|
||||
void expireNow() {
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
SessionInformation sessionInfo = this.sessionRegistry.getSessionInformation(SESSION_ID);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
|
||||
sessionInfo.expireNow();
|
||||
@@ -146,13 +150,11 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
ArgumentCaptor<Session> captor = ArgumentCaptor.forClass(Session.class);
|
||||
verify(this.sessionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().<Boolean>getAttribute(
|
||||
SpringSessionBackedSessionInformation.EXPIRED_ATTR))
|
||||
.isEqualTo(Boolean.TRUE);
|
||||
assertThat(captor.getValue().<Boolean>getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))
|
||||
.isEqualTo(Boolean.TRUE);
|
||||
}
|
||||
|
||||
private Session createSession(String sessionId, String userName,
|
||||
Instant lastAccessed) {
|
||||
private Session createSession(String sessionId, String userName, Instant lastAccessed) {
|
||||
MapSession session = new MapSession(sessionId);
|
||||
session.setLastAccessedTime(lastAccessed);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -165,14 +167,48 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
private void setUpSessions() {
|
||||
Session session1 = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
session1.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR,
|
||||
Boolean.TRUE);
|
||||
session1.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, Boolean.TRUE);
|
||||
Session session2 = createSession(SESSION_ID2, USER_NAME, NOW);
|
||||
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.findByPrincipalName(USER_NAME)).thenReturn(sessions);
|
||||
}
|
||||
|
||||
private static final class TestPrincipal implements Principal {
|
||||
|
||||
private final String name;
|
||||
|
||||
private TestPrincipal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object another) {
|
||||
if (this == another) {
|
||||
return true;
|
||||
}
|
||||
if (another instanceof TestPrincipal) {
|
||||
return this.name.equals(((TestPrincipal) another).name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,60 +40,50 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class SpringSessionRememberMeServicesTests {
|
||||
class SpringSessionRememberMeServicesTests {
|
||||
|
||||
private SpringSessionRememberMeServices rememberMeServices;
|
||||
|
||||
@Test
|
||||
public void create() {
|
||||
void create() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices,
|
||||
"rememberMeParameterName")).isEqualTo("remember-me");
|
||||
assertThat(
|
||||
ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember"))
|
||||
.isEqualTo(false);
|
||||
assertThat(
|
||||
ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds"))
|
||||
.isEqualTo(2592000);
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "rememberMeParameterName"))
|
||||
.isEqualTo("remember-me");
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember")).isEqualTo(false);
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds")).isEqualTo(2592000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithCustomParameter() {
|
||||
void createWithCustomParameter() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
this.rememberMeServices.setRememberMeParameterName("test-param");
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices,
|
||||
"rememberMeParameterName")).isEqualTo("test-param");
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "rememberMeParameterName"))
|
||||
.isEqualTo("test-param");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithNullParameter() {
|
||||
void createWithNullParameter() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(
|
||||
() -> this.rememberMeServices.setRememberMeParameterName(null))
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.rememberMeServices.setRememberMeParameterName(null))
|
||||
.withMessage("rememberMeParameterName cannot be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithAlwaysRemember() {
|
||||
void createWithAlwaysRemember() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
this.rememberMeServices.setAlwaysRemember(true);
|
||||
assertThat(
|
||||
ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember"))
|
||||
.isEqualTo(true);
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember")).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithCustomValidity() {
|
||||
void createWithCustomValidity() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
this.rememberMeServices.setValiditySeconds(100000);
|
||||
assertThat(
|
||||
ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds"))
|
||||
.isEqualTo(100000);
|
||||
assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds")).isEqualTo(100000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoLogin() {
|
||||
void autoLogin() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
@@ -103,7 +93,7 @@ public class SpringSessionRememberMeServicesTests {
|
||||
|
||||
// gh-752
|
||||
@Test
|
||||
public void loginFailRemoveSecurityContext() {
|
||||
void loginFailRemoveSecurityContext() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
HttpSession session = mock(HttpSession.class);
|
||||
@@ -111,13 +101,12 @@ public class SpringSessionRememberMeServicesTests {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
this.rememberMeServices.loginFail(request, response);
|
||||
verify(request, times(1)).getSession(eq(false));
|
||||
verify(session, times(1)).removeAttribute(
|
||||
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||
verify(session, times(1)).removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||
verifyZeroInteractions(request, response, session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccess() {
|
||||
void loginSuccess() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -128,14 +117,13 @@ public class SpringSessionRememberMeServicesTests {
|
||||
this.rememberMeServices.loginSuccess(request, response, authentication);
|
||||
verify(request, times(1)).getParameter(eq("remember-me"));
|
||||
verify(request, times(1)).getSession();
|
||||
verify(request, times(1)).setAttribute(
|
||||
eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(request, times(1)).setAttribute(eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(session, times(1)).setMaxInactiveInterval(eq(2592000));
|
||||
verifyZeroInteractions(request, response, session, authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccessWithCustomParameter() {
|
||||
void loginSuccessWithCustomParameter() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -147,14 +135,13 @@ public class SpringSessionRememberMeServicesTests {
|
||||
this.rememberMeServices.loginSuccess(request, response, authentication);
|
||||
verify(request, times(1)).getParameter(eq("test-param"));
|
||||
verify(request, times(1)).getSession();
|
||||
verify(request, times(1)).setAttribute(
|
||||
eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(request, times(1)).setAttribute(eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(session, times(1)).setMaxInactiveInterval(eq(2592000));
|
||||
verifyZeroInteractions(request, response, session, authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccessWithAlwaysRemember() {
|
||||
void loginSuccessWithAlwaysRemember() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -164,14 +151,13 @@ public class SpringSessionRememberMeServicesTests {
|
||||
this.rememberMeServices.setAlwaysRemember(true);
|
||||
this.rememberMeServices.loginSuccess(request, response, authentication);
|
||||
verify(request, times(1)).getSession();
|
||||
verify(request, times(1)).setAttribute(
|
||||
eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(request, times(1)).setAttribute(eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(session, times(1)).setMaxInactiveInterval(eq(2592000));
|
||||
verifyZeroInteractions(request, response, session, authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccessWithCustomValidity() {
|
||||
void loginSuccessWithCustomValidity() {
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -183,8 +169,7 @@ public class SpringSessionRememberMeServicesTests {
|
||||
this.rememberMeServices.loginSuccess(request, response, authentication);
|
||||
verify(request, times(1)).getParameter(eq("remember-me"));
|
||||
verify(request, times(1)).getSession();
|
||||
verify(request, times(1)).setAttribute(
|
||||
eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(request, times(1)).setAttribute(eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true));
|
||||
verify(session, times(1)).setMaxInactiveInterval(eq(100000));
|
||||
verifyZeroInteractions(request, response, session, authentication);
|
||||
}
|
||||
|
||||
@@ -34,17 +34,20 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Tests for {@link CookieHttpSessionIdResolver}.
|
||||
*/
|
||||
public class CookieHttpSessionIdResolverTests {
|
||||
class CookieHttpSessionIdResolverTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private CookieHttpSessionIdResolver strategy;
|
||||
|
||||
private String cookieName;
|
||||
|
||||
private Session session;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
void setup() {
|
||||
this.cookieName = "SESSION";
|
||||
this.session = new MapSession();
|
||||
this.request = new MockHttpServletRequest();
|
||||
@@ -53,19 +56,19 @@ public class CookieHttpSessionIdResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() throws Exception {
|
||||
void getRequestedSessionIdNull() {
|
||||
assertThat(this.strategy.resolveSessionIds(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() throws Exception {
|
||||
void getRequestedSessionIdNotNull() {
|
||||
setSessionCookie(this.session.getId());
|
||||
assertThat(this.strategy.resolveSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(this.session.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNullCustomCookieName() throws Exception {
|
||||
void getRequestedSessionIdNotNullCustomCookieName() {
|
||||
setCookieName("CUSTOM");
|
||||
setSessionCookie(this.session.getId());
|
||||
assertThat(this.strategy.resolveSessionIds(this.request))
|
||||
@@ -73,13 +76,13 @@ public class CookieHttpSessionIdResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSession() throws Exception {
|
||||
void onNewSession() {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
assertThat(getSessionId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionTwiceSameId() throws Exception {
|
||||
void onNewSessionTwiceSameId() {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
@@ -87,7 +90,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionTwiceNewId() throws Exception {
|
||||
void onNewSessionTwiceNewId() {
|
||||
Session newSession = new MapSession();
|
||||
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
@@ -101,49 +104,47 @@ public class CookieHttpSessionIdResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionCookiePath() throws Exception {
|
||||
void onNewSessionCookiePath() {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionCustomCookieName() throws Exception {
|
||||
void onNewSessionCustomCookieName() {
|
||||
setCookieName("CUSTOM");
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
assertThat(getSessionId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSession() throws Exception {
|
||||
void onDeleteSession() {
|
||||
this.strategy.expireSession(this.request, this.response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionCookiePath() throws Exception {
|
||||
void onDeleteSessionCookiePath() {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.expireSession(this.request, this.response);
|
||||
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionCustomCookieName() throws Exception {
|
||||
void onDeleteSessionCustomCookieName() {
|
||||
setCookieName("CUSTOM");
|
||||
this.strategy.expireSession(this.request, this.response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionCookieValue() {
|
||||
assertThat(createSessionCookieValue(17)).isEqualToIgnoringCase(
|
||||
"0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16");
|
||||
void createSessionCookieValue() {
|
||||
assertThat(createSessionCookieValue(17))
|
||||
.isEqualToIgnoringCase("0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16");
|
||||
}
|
||||
|
||||
private String createSessionCookieValue(long size) {
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
@@ -42,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* @author Vedran Pavic
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
public class DefaultCookieSerializerTests {
|
||||
class DefaultCookieSerializerTests {
|
||||
|
||||
private String cookieName;
|
||||
|
||||
@@ -55,7 +60,7 @@ public class DefaultCookieSerializerTests {
|
||||
private String sessionId;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.cookieName = "SESSION";
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
@@ -66,459 +71,397 @@ public class DefaultCookieSerializerTests {
|
||||
// --- readCookieValues ---
|
||||
|
||||
@Test
|
||||
public void readCookieValuesNull() {
|
||||
void readCookieValuesNull() {
|
||||
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesSingle(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesSingle(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsOnly(this.sessionId);
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding));
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readCookieSerializerUseBase64EncodingTrueValuesNotBase64() {
|
||||
void readCookieSerializerUseBase64EncodingTrueValuesNotBase64() {
|
||||
this.sessionId = "&^%$*";
|
||||
this.serializer.setUseBase64Encoding(true);
|
||||
this.request.setCookies(new Cookie(this.cookieName, this.sessionId));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesSingleAndInvalidName(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesSingleAndInvalidName(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
createCookie(this.cookieName + "INVALID", this.sessionId + "INVALID",
|
||||
useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsOnly(this.sessionId);
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
createCookie(this.cookieName + "INVALID", this.sessionId + "INVALID", useBase64Encoding));
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesMulti(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesMulti(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
String secondSession = "secondSessionId";
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
createCookie(this.cookieName, secondSession, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsExactly(this.sessionId, secondSession);
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesMultiCustomSessionCookieName(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesMultiCustomSessionCookieName(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
setCookieName("JSESSIONID");
|
||||
String secondSession = "secondSessionId";
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
|
||||
createCookie(this.cookieName, secondSession, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsExactly(this.sessionId, secondSession);
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
|
||||
}
|
||||
|
||||
// gh-392
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesNullCookieValue(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesNullCookieValue(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesNullCookieValueAndJvmRoute(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesNullCookieValueAndJvmRoute(boolean useBase64Encoding) {
|
||||
this.serializer.setJvmRoute("123");
|
||||
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieValuesNullCookieValueAndNotNullCookie(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieValuesNullCookieValueAndNotNullCookie(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
this.serializer.setJvmRoute("123");
|
||||
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding),
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsOnly(this.sessionId);
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
|
||||
}
|
||||
|
||||
// --- writeCookie ---
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void writeCookie(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void writeCookie(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookieValue(useBase64Encoding)).isEqualTo(this.sessionId);
|
||||
}
|
||||
|
||||
// --- httpOnly ---
|
||||
|
||||
@Test
|
||||
public void writeCookieHttpOnlyDefault() {
|
||||
void writeCookieHttpOnlyDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isHttpOnly()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieHttpOnlySetTrue() {
|
||||
void writeCookieHttpOnlySetTrue() {
|
||||
this.serializer.setUseHttpOnlyCookie(true);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isHttpOnly()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieHttpOnlySetFalse() {
|
||||
void writeCookieHttpOnlySetFalse() {
|
||||
this.serializer.setUseHttpOnlyCookie(false);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isHttpOnly()).isFalse();
|
||||
}
|
||||
|
||||
// --- domainName ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDomainNameDefault() {
|
||||
void writeCookieDomainNameDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getDomain()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieDomainNameCustom() {
|
||||
void writeCookieDomainNameCustom() {
|
||||
String domainName = "example.com";
|
||||
this.serializer.setDomainName(domainName);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getDomain()).isEqualTo(domainName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDomainNameAndDomainNamePatternThrows() {
|
||||
void setDomainNameAndDomainNamePatternThrows() {
|
||||
this.serializer.setDomainName("example.com");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.serializer.setDomainNamePattern(".*"))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.serializer.setDomainNamePattern(".*"))
|
||||
.withMessage("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
|
||||
// --- domainNamePattern ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDomainNamePattern() {
|
||||
void writeCookieDomainNamePattern() {
|
||||
String domainNamePattern = "^.+?\\.(\\w+\\.[a-z]+)$";
|
||||
this.serializer.setDomainNamePattern(domainNamePattern);
|
||||
|
||||
String[] matchingDomains = { "child.sub.example.com", "www.example.com" };
|
||||
for (String domain : matchingDomains) {
|
||||
this.request.setServerName(domain);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getDomain()).isEqualTo("example.com");
|
||||
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
String[] notMatchingDomains = { "example.com", "localhost", "127.0.0.1" };
|
||||
for (String domain : notMatchingDomains) {
|
||||
this.request.setServerName(domain);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getDomain()).isNull();
|
||||
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDomainNamePatternAndDomainNameThrows() {
|
||||
void setDomainNamePatternAndDomainNameThrows() {
|
||||
this.serializer.setDomainNamePattern(".*");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.serializer.setDomainName("example.com"))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.serializer.setDomainName("example.com"))
|
||||
.withMessage("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
|
||||
// --- cookieName ---
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieNameDefault() {
|
||||
void writeCookieCookieNameDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getName()).isEqualTo("SESSION");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieNameCustom() {
|
||||
void writeCookieCookieNameCustom() {
|
||||
String cookieName = "JSESSIONID";
|
||||
setCookieName(cookieName);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getName()).isEqualTo(cookieName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCookieNameNullThrows() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.serializer.setCookieName(null))
|
||||
void setCookieNameNullThrows() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.serializer.setCookieName(null))
|
||||
.withMessage("cookieName cannot be null");
|
||||
}
|
||||
|
||||
// --- cookiePath ---
|
||||
|
||||
@Test
|
||||
public void writeCookieCookiePathDefaultEmptyContextPathUsed() {
|
||||
void writeCookieCookiePathDefaultEmptyContextPathUsed() {
|
||||
this.request.setContextPath("");
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getPath()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookiePathDefaultContextPathUsed() {
|
||||
void writeCookieCookiePathDefaultContextPathUsed() {
|
||||
this.request.setContextPath("/context");
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getPath()).isEqualTo("/context/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookiePathExplicitNullCookiePathContextPathUsed() {
|
||||
void writeCookieCookiePathExplicitNullCookiePathContextPathUsed() {
|
||||
this.request.setContextPath("/context");
|
||||
this.serializer.setCookiePath(null);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getPath()).isEqualTo("/context/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookiePathExplicitCookiePath() {
|
||||
void writeCookieCookiePathExplicitCookiePath() {
|
||||
this.request.setContextPath("/context");
|
||||
this.serializer.setCookiePath("/");
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getPath()).isEqualTo("/");
|
||||
}
|
||||
|
||||
// --- cookieMaxAge ---
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieMaxAgeDefault() {
|
||||
void writeCookieCookieMaxAgeDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(-1);
|
||||
assertThat(getCookie().getExpires()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieMaxAgeExplicit() {
|
||||
void writeCookieCookieMaxAgeExplicit() {
|
||||
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
|
||||
this.serializer.setCookieMaxAge(100);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(100);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieMaxAgeExplicitEmptyCookie() {
|
||||
void writeCookieCookieMaxAgeExplicitEmptyCookie() {
|
||||
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
|
||||
this.serializer.setCookieMaxAge(100);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(""));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(0);
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(0);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Thu, 1 Jan 1970 00:00:00 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieCookieMaxAgeExplicitCookieValue() {
|
||||
void writeCookieCookieMaxAgeExplicitCookieValue() {
|
||||
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
|
||||
CookieValue cookieValue = cookieValue(this.sessionId);
|
||||
cookieValue.setCookieMaxAge(100);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(100);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
}
|
||||
|
||||
// --- secure ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDefaultInsecureRequest() {
|
||||
void writeCookieDefaultInsecureRequest() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSecureSecureRequest() {
|
||||
void writeCookieSecureSecureRequest() {
|
||||
this.request.setSecure(true);
|
||||
this.serializer.setUseSecureCookie(true);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSecureInsecureRequest() {
|
||||
void writeCookieSecureInsecureRequest() {
|
||||
this.serializer.setUseSecureCookie(true);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieInsecureSecureRequest() {
|
||||
void writeCookieInsecureSecureRequest() {
|
||||
this.request.setSecure(true);
|
||||
this.serializer.setUseSecureCookie(false);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieInecureInsecureRequest() {
|
||||
void writeCookieInecureInsecureRequest() {
|
||||
this.serializer.setUseSecureCookie(false);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
// --- jvmRoute ---
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void writeCookieJvmRoute(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void writeCookieJvmRoute(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
String jvmRoute = "route";
|
||||
this.serializer.setJvmRoute(jvmRoute);
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookieValue(useBase64Encoding))
|
||||
.isEqualTo(this.sessionId + "." + jvmRoute);
|
||||
assertThat(getCookieValue(useBase64Encoding)).isEqualTo(this.sessionId + "." + jvmRoute);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieJvmRoute(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieJvmRoute(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
String jvmRoute = "route";
|
||||
this.serializer.setJvmRoute(jvmRoute);
|
||||
this.request.setCookies(createCookie(this.cookieName,
|
||||
this.sessionId + "." + jvmRoute, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsOnly(this.sessionId);
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId + "." + jvmRoute, useBase64Encoding));
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieJvmRouteRouteMissing(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieJvmRouteRouteMissing(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
String jvmRoute = "route";
|
||||
this.serializer.setJvmRoute(jvmRoute);
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, this.sessionId, useBase64Encoding));
|
||||
|
||||
assertThat(this.serializer.readCookieValues(this.request))
|
||||
.containsOnly(this.sessionId);
|
||||
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding));
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "true", "false" })
|
||||
public void readCookieJvmRouteOnlyRoute(boolean useBase64Encoding) {
|
||||
@ValueSource(booleans = { true, false })
|
||||
void readCookieJvmRouteOnlyRoute(boolean useBase64Encoding) {
|
||||
this.serializer.setUseBase64Encoding(useBase64Encoding);
|
||||
String jvmRoute = "route";
|
||||
this.serializer.setJvmRoute(jvmRoute);
|
||||
this.request.setCookies(
|
||||
createCookie(this.cookieName, "." + jvmRoute, useBase64Encoding));
|
||||
|
||||
this.request.setCookies(createCookie(this.cookieName, "." + jvmRoute, useBase64Encoding));
|
||||
assertThat(this.serializer.readCookieValues(this.request)).containsOnly("");
|
||||
}
|
||||
|
||||
// --- rememberMe ---
|
||||
|
||||
@Test
|
||||
public void writeCookieRememberMeCookieMaxAgeDefault() {
|
||||
void writeCookieRememberMeCookieMaxAgeDefault() {
|
||||
this.request.setAttribute("rememberMe", true);
|
||||
this.serializer.setRememberMeRequestAttribute("rememberMe");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieRememberMeCookieMaxAgeOverride() {
|
||||
void writeCookieRememberMeCookieMaxAgeOverride() {
|
||||
this.request.setAttribute("rememberMe", true);
|
||||
this.serializer.setRememberMeRequestAttribute("rememberMe");
|
||||
CookieValue cookieValue = cookieValue(this.sessionId);
|
||||
cookieValue.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- sameSite ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDefaultSameSiteLax() {
|
||||
void writeCookieDefaultSameSiteLax() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteLax() {
|
||||
void writeCookieSetSameSiteLax() {
|
||||
this.serializer.setSameSite("Lax");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteStrict() {
|
||||
void writeCookieSetSameSiteStrict() {
|
||||
this.serializer.setSameSite("Strict");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Strict");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteNull() {
|
||||
void writeCookieSetSameSiteNull() {
|
||||
this.serializer.setSameSite(null);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isNull();
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
void setCookieName(String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
this.serializer.setCookieName(cookieName);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
/**
|
||||
* Tests for {@link HeaderHttpSessionIdResolver}.
|
||||
*/
|
||||
public class HeaderHttpSessionIdResolverTests {
|
||||
class HeaderHttpSessionIdResolverTests {
|
||||
|
||||
private static final String HEADER_X_AUTH_TOKEN = "X-Auth-Token";
|
||||
|
||||
@@ -43,71 +43,64 @@ public class HeaderHttpSessionIdResolverTests {
|
||||
private HeaderHttpSessionIdResolver resolver;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.resolver = HeaderHttpSessionIdResolver.xAuthToken();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResolverWithXAuthTokenHeader() {
|
||||
void createResolverWithXAuthTokenHeader() {
|
||||
HeaderHttpSessionIdResolver resolver = HeaderHttpSessionIdResolver.xAuthToken();
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName"))
|
||||
.isEqualTo("X-Auth-Token");
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName")).isEqualTo("X-Auth-Token");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResolverWithAuthenticationInfoHeader() {
|
||||
HeaderHttpSessionIdResolver resolver = HeaderHttpSessionIdResolver
|
||||
.authenticationInfo();
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName"))
|
||||
.isEqualTo("Authentication-Info");
|
||||
void createResolverWithAuthenticationInfoHeader() {
|
||||
HeaderHttpSessionIdResolver resolver = HeaderHttpSessionIdResolver.authenticationInfo();
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName")).isEqualTo("Authentication-Info");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResolverWithCustomHeaderName() {
|
||||
HeaderHttpSessionIdResolver resolver = new HeaderHttpSessionIdResolver(
|
||||
"Custom-Header");
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName"))
|
||||
.isEqualTo("Custom-Header");
|
||||
void createResolverWithCustomHeaderName() {
|
||||
HeaderHttpSessionIdResolver resolver = new HeaderHttpSessionIdResolver("Custom-Header");
|
||||
assertThat(ReflectionTestUtils.getField(resolver, "headerName")).isEqualTo("Custom-Header");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResolverWithNullHeaderName() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new HeaderHttpSessionIdResolver(null))
|
||||
void createResolverWithNullHeaderName() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new HeaderHttpSessionIdResolver(null))
|
||||
.withMessage("headerName cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() {
|
||||
void getRequestedSessionIdNull() {
|
||||
assertThat(this.resolver.resolveSessionIds(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() {
|
||||
void getRequestedSessionIdNotNull() {
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
setSessionId(sessionId);
|
||||
assertThat(this.resolver.resolveSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(sessionId));
|
||||
assertThat(this.resolver.resolveSessionIds(this.request)).isEqualTo(Collections.singletonList(sessionId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSession() {
|
||||
void onNewSession() {
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
this.resolver.setSessionId(this.request, this.response, sessionId);
|
||||
assertThat(getSessionId()).isEqualTo(sessionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSession() {
|
||||
void onDeleteSession() {
|
||||
this.resolver.expireSession(this.request, this.response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
// the header is set as apposed to added
|
||||
@Test
|
||||
public void onNewSessionMulti() {
|
||||
void onNewSessionMulti() {
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
this.resolver.setSessionId(this.request, this.response, sessionId);
|
||||
this.resolver.setSessionId(this.request, this.response, sessionId);
|
||||
@@ -117,7 +110,7 @@ public class HeaderHttpSessionIdResolverTests {
|
||||
|
||||
// the header is set as apposed to added
|
||||
@Test
|
||||
public void onDeleteSessionMulti() {
|
||||
void onDeleteSessionMulti() {
|
||||
this.resolver.expireSession(this.request, this.response);
|
||||
this.resolver.expireSession(this.request, this.response);
|
||||
assertThat(this.response.getHeaders(HEADER_X_AUTH_TOKEN).size()).isEqualTo(1);
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.session.web.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
@@ -32,22 +33,25 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class OnCommittedResponseWrapperTests {
|
||||
class OnCommittedResponseWrapperTests {
|
||||
|
||||
private static final String NL = "\r\n";
|
||||
|
||||
@Mock
|
||||
HttpServletResponse delegate;
|
||||
|
||||
@Mock
|
||||
PrintWriter writer;
|
||||
|
||||
@Mock
|
||||
ServletOutputStream out;
|
||||
|
||||
OnCommittedResponseWrapper response;
|
||||
private OnCommittedResponseWrapper response;
|
||||
|
||||
boolean committed;
|
||||
private boolean committed;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.response = new OnCommittedResponseWrapper(this.delegate) {
|
||||
@Override
|
||||
@@ -62,14 +66,14 @@ public class OnCommittedResponseWrapperTests {
|
||||
// --- printwriter
|
||||
|
||||
@Test
|
||||
public void printWriterHashCode() throws Exception {
|
||||
void printWriterHashCode() throws Exception {
|
||||
int expected = this.writer.hashCode();
|
||||
|
||||
assertThat(this.response.getWriter().hashCode()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterCheckError() throws Exception {
|
||||
void printWriterCheckError() throws Exception {
|
||||
boolean expected = true;
|
||||
given(this.writer.checkError()).willReturn(expected);
|
||||
|
||||
@@ -77,7 +81,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteInt() throws Exception {
|
||||
void printWriterWriteInt() throws Exception {
|
||||
int expected = 1;
|
||||
|
||||
this.response.getWriter().write(expected);
|
||||
@@ -86,7 +90,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteCharIntInt() throws Exception {
|
||||
void printWriterWriteCharIntInt() throws Exception {
|
||||
char[] buff = new char[0];
|
||||
int off = 2;
|
||||
int len = 3;
|
||||
@@ -97,7 +101,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteChar() throws Exception {
|
||||
void printWriterWriteChar() throws Exception {
|
||||
char[] buff = new char[0];
|
||||
|
||||
this.response.getWriter().write(buff);
|
||||
@@ -106,7 +110,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteStringIntInt() throws Exception {
|
||||
void printWriterWriteStringIntInt() throws Exception {
|
||||
String s = "";
|
||||
int off = 2;
|
||||
int len = 3;
|
||||
@@ -117,7 +121,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteString() throws Exception {
|
||||
void printWriterWriteString() throws Exception {
|
||||
String s = "";
|
||||
|
||||
this.response.getWriter().write(s);
|
||||
@@ -126,7 +130,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintBoolean() throws Exception {
|
||||
void printWriterPrintBoolean() throws Exception {
|
||||
boolean b = true;
|
||||
|
||||
this.response.getWriter().print(b);
|
||||
@@ -135,7 +139,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintChar() throws Exception {
|
||||
void printWriterPrintChar() throws Exception {
|
||||
char c = 1;
|
||||
|
||||
this.response.getWriter().print(c);
|
||||
@@ -144,7 +148,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintInt() throws Exception {
|
||||
void printWriterPrintInt() throws Exception {
|
||||
int i = 1;
|
||||
|
||||
this.response.getWriter().print(i);
|
||||
@@ -153,7 +157,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintLong() throws Exception {
|
||||
void printWriterPrintLong() throws Exception {
|
||||
long l = 1;
|
||||
|
||||
this.response.getWriter().print(l);
|
||||
@@ -162,7 +166,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintFloat() throws Exception {
|
||||
void printWriterPrintFloat() throws Exception {
|
||||
float f = 1;
|
||||
|
||||
this.response.getWriter().print(f);
|
||||
@@ -171,7 +175,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintDouble() throws Exception {
|
||||
void printWriterPrintDouble() throws Exception {
|
||||
double x = 1;
|
||||
|
||||
this.response.getWriter().print(x);
|
||||
@@ -180,7 +184,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintCharArray() throws Exception {
|
||||
void printWriterPrintCharArray() throws Exception {
|
||||
char[] x = new char[0];
|
||||
|
||||
this.response.getWriter().print(x);
|
||||
@@ -189,7 +193,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintString() throws Exception {
|
||||
void printWriterPrintString() throws Exception {
|
||||
String x = "1";
|
||||
|
||||
this.response.getWriter().print(x);
|
||||
@@ -198,7 +202,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintObject() throws Exception {
|
||||
void printWriterPrintObject() throws Exception {
|
||||
Object x = "1";
|
||||
|
||||
this.response.getWriter().print(x);
|
||||
@@ -207,14 +211,14 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintln() throws Exception {
|
||||
void printWriterPrintln() throws Exception {
|
||||
this.response.getWriter().println();
|
||||
|
||||
verify(this.writer).println();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnBoolean() throws Exception {
|
||||
void printWriterPrintlnBoolean() throws Exception {
|
||||
boolean b = true;
|
||||
|
||||
this.response.getWriter().println(b);
|
||||
@@ -223,7 +227,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnChar() throws Exception {
|
||||
void printWriterPrintlnChar() throws Exception {
|
||||
char c = 1;
|
||||
|
||||
this.response.getWriter().println(c);
|
||||
@@ -232,7 +236,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnInt() throws Exception {
|
||||
void printWriterPrintlnInt() throws Exception {
|
||||
int i = 1;
|
||||
|
||||
this.response.getWriter().println(i);
|
||||
@@ -241,7 +245,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnLong() throws Exception {
|
||||
void printWriterPrintlnLong() throws Exception {
|
||||
long l = 1;
|
||||
|
||||
this.response.getWriter().println(l);
|
||||
@@ -250,7 +254,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnFloat() throws Exception {
|
||||
void printWriterPrintlnFloat() throws Exception {
|
||||
float f = 1;
|
||||
|
||||
this.response.getWriter().println(f);
|
||||
@@ -259,7 +263,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnDouble() throws Exception {
|
||||
void printWriterPrintlnDouble() throws Exception {
|
||||
double x = 1;
|
||||
|
||||
this.response.getWriter().println(x);
|
||||
@@ -268,7 +272,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnCharArray() throws Exception {
|
||||
void printWriterPrintlnCharArray() throws Exception {
|
||||
char[] x = new char[0];
|
||||
|
||||
this.response.getWriter().println(x);
|
||||
@@ -277,7 +281,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnString() throws Exception {
|
||||
void printWriterPrintlnString() throws Exception {
|
||||
String x = "1";
|
||||
|
||||
this.response.getWriter().println(x);
|
||||
@@ -286,7 +290,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintlnObject() throws Exception {
|
||||
void printWriterPrintlnObject() throws Exception {
|
||||
Object x = "1";
|
||||
|
||||
this.response.getWriter().println(x);
|
||||
@@ -295,7 +299,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintfStringObjectVargs() throws Exception {
|
||||
void printWriterPrintfStringObjectVargs() throws Exception {
|
||||
String format = "format";
|
||||
Object[] args = new Object[] { "1" };
|
||||
|
||||
@@ -305,7 +309,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterPrintfLocaleStringObjectVargs() throws Exception {
|
||||
void printWriterPrintfLocaleStringObjectVargs() throws Exception {
|
||||
Locale l = Locale.US;
|
||||
String format = "format";
|
||||
Object[] args = new Object[] { "1" };
|
||||
@@ -316,7 +320,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterFormatStringObjectVargs() throws Exception {
|
||||
void printWriterFormatStringObjectVargs() throws Exception {
|
||||
String format = "format";
|
||||
Object[] args = new Object[] { "1" };
|
||||
|
||||
@@ -326,7 +330,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterFormatLocaleStringObjectVargs() throws Exception {
|
||||
void printWriterFormatLocaleStringObjectVargs() throws Exception {
|
||||
Locale l = Locale.US;
|
||||
String format = "format";
|
||||
Object[] args = new Object[] { "1" };
|
||||
@@ -337,7 +341,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterAppendCharSequence() throws Exception {
|
||||
void printWriterAppendCharSequence() throws Exception {
|
||||
String x = "a";
|
||||
|
||||
this.response.getWriter().append(x);
|
||||
@@ -346,7 +350,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterAppendCharSequenceIntInt() throws Exception {
|
||||
void printWriterAppendCharSequenceIntInt() throws Exception {
|
||||
String x = "abcdef";
|
||||
int start = 1;
|
||||
int end = 3;
|
||||
@@ -357,7 +361,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterAppendChar() throws Exception {
|
||||
void printWriterAppendChar() throws Exception {
|
||||
char x = 1;
|
||||
|
||||
this.response.getWriter().append(x);
|
||||
@@ -368,14 +372,14 @@ public class OnCommittedResponseWrapperTests {
|
||||
// servletoutputstream
|
||||
|
||||
@Test
|
||||
public void outputStreamHashCode() throws Exception {
|
||||
void outputStreamHashCode() throws Exception {
|
||||
int expected = this.out.hashCode();
|
||||
|
||||
assertThat(this.response.getOutputStream().hashCode()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamWriteInt() throws Exception {
|
||||
void outputStreamWriteInt() throws Exception {
|
||||
int expected = 1;
|
||||
|
||||
this.response.getOutputStream().write(expected);
|
||||
@@ -384,7 +388,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamWriteByte() throws Exception {
|
||||
void outputStreamWriteByte() throws Exception {
|
||||
byte[] expected = new byte[0];
|
||||
|
||||
this.response.getOutputStream().write(expected);
|
||||
@@ -393,7 +397,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamWriteByteIntInt() throws Exception {
|
||||
void outputStreamWriteByteIntInt() throws Exception {
|
||||
int start = 1;
|
||||
int end = 2;
|
||||
byte[] expected = new byte[0];
|
||||
@@ -404,7 +408,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintBoolean() throws Exception {
|
||||
void outputStreamPrintBoolean() throws Exception {
|
||||
boolean b = true;
|
||||
|
||||
this.response.getOutputStream().print(b);
|
||||
@@ -413,7 +417,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintChar() throws Exception {
|
||||
void outputStreamPrintChar() throws Exception {
|
||||
char c = 1;
|
||||
|
||||
this.response.getOutputStream().print(c);
|
||||
@@ -422,7 +426,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintInt() throws Exception {
|
||||
void outputStreamPrintInt() throws Exception {
|
||||
int i = 1;
|
||||
|
||||
this.response.getOutputStream().print(i);
|
||||
@@ -431,7 +435,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintLong() throws Exception {
|
||||
void outputStreamPrintLong() throws Exception {
|
||||
long l = 1;
|
||||
|
||||
this.response.getOutputStream().print(l);
|
||||
@@ -440,7 +444,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintFloat() throws Exception {
|
||||
void outputStreamPrintFloat() throws Exception {
|
||||
float f = 1;
|
||||
|
||||
this.response.getOutputStream().print(f);
|
||||
@@ -449,7 +453,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintDouble() throws Exception {
|
||||
void outputStreamPrintDouble() throws Exception {
|
||||
double x = 1;
|
||||
|
||||
this.response.getOutputStream().print(x);
|
||||
@@ -458,7 +462,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintString() throws Exception {
|
||||
void outputStreamPrintString() throws Exception {
|
||||
String x = "1";
|
||||
|
||||
this.response.getOutputStream().print(x);
|
||||
@@ -467,14 +471,14 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintln() throws Exception {
|
||||
void outputStreamPrintln() throws Exception {
|
||||
this.response.getOutputStream().println();
|
||||
|
||||
verify(this.out).println();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnBoolean() throws Exception {
|
||||
void outputStreamPrintlnBoolean() throws Exception {
|
||||
boolean b = true;
|
||||
|
||||
this.response.getOutputStream().println(b);
|
||||
@@ -483,7 +487,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnChar() throws Exception {
|
||||
void outputStreamPrintlnChar() throws Exception {
|
||||
char c = 1;
|
||||
|
||||
this.response.getOutputStream().println(c);
|
||||
@@ -492,7 +496,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnInt() throws Exception {
|
||||
void outputStreamPrintlnInt() throws Exception {
|
||||
int i = 1;
|
||||
|
||||
this.response.getOutputStream().println(i);
|
||||
@@ -501,7 +505,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnLong() throws Exception {
|
||||
void outputStreamPrintlnLong() throws Exception {
|
||||
long l = 1;
|
||||
|
||||
this.response.getOutputStream().println(l);
|
||||
@@ -510,7 +514,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnFloat() throws Exception {
|
||||
void outputStreamPrintlnFloat() throws Exception {
|
||||
float f = 1;
|
||||
|
||||
this.response.getOutputStream().println(f);
|
||||
@@ -519,7 +523,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnDouble() throws Exception {
|
||||
void outputStreamPrintlnDouble() throws Exception {
|
||||
double x = 1;
|
||||
|
||||
this.response.getOutputStream().println(x);
|
||||
@@ -528,7 +532,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStreamPrintlnString() throws Exception {
|
||||
void outputStreamPrintlnString() throws Exception {
|
||||
String x = "1";
|
||||
|
||||
this.response.getOutputStream().println(x);
|
||||
@@ -540,7 +544,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
// has been greater than zero and has been written to the response.
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteIntCommits() throws Exception {
|
||||
void contentLengthPrintWriterWriteIntCommits() throws Exception {
|
||||
int expected = 1;
|
||||
this.response.setContentLength(String.valueOf(expected).length());
|
||||
|
||||
@@ -550,7 +554,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteIntMultiDigitCommits() throws Exception {
|
||||
void contentLengthPrintWriterWriteIntMultiDigitCommits() throws Exception {
|
||||
int expected = 10000;
|
||||
this.response.setContentLength(String.valueOf(expected).length());
|
||||
|
||||
@@ -560,8 +564,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPlus1PrintWriterWriteIntMultiDigitCommits()
|
||||
throws Exception {
|
||||
void contentLengthPlus1PrintWriterWriteIntMultiDigitCommits() throws Exception {
|
||||
int expected = 10000;
|
||||
this.response.setContentLength(String.valueOf(expected).length() + 1);
|
||||
|
||||
@@ -575,7 +578,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteCharIntIntCommits() throws Exception {
|
||||
void contentLengthPrintWriterWriteCharIntIntCommits() throws Exception {
|
||||
char[] buff = new char[0];
|
||||
int off = 2;
|
||||
int len = 3;
|
||||
@@ -587,7 +590,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteCharCommits() throws Exception {
|
||||
void contentLengthPrintWriterWriteCharCommits() throws Exception {
|
||||
char[] buff = new char[4];
|
||||
this.response.setContentLength(buff.length);
|
||||
|
||||
@@ -597,7 +600,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteStringIntIntCommits() throws Exception {
|
||||
void contentLengthPrintWriterWriteStringIntIntCommits() throws Exception {
|
||||
String s = "";
|
||||
int off = 2;
|
||||
int len = 3;
|
||||
@@ -609,7 +612,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterWriteStringCommits() throws IOException {
|
||||
void contentLengthPrintWriterWriteStringCommits() throws IOException {
|
||||
String body = "something";
|
||||
this.response.setContentLength(body.length());
|
||||
|
||||
@@ -619,7 +622,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteStringContentLengthCommits() throws IOException {
|
||||
void printWriterWriteStringContentLengthCommits() throws IOException {
|
||||
String body = "something";
|
||||
this.response.getWriter().write(body);
|
||||
|
||||
@@ -629,7 +632,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void printWriterWriteStringDoesNotCommit() throws IOException {
|
||||
void printWriterWriteStringDoesNotCommit() throws IOException {
|
||||
String body = "something";
|
||||
|
||||
this.response.getWriter().write(body);
|
||||
@@ -638,7 +641,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintBooleanCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintBooleanCommits() throws Exception {
|
||||
boolean b = true;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -648,7 +651,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintCharCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintCharCommits() throws Exception {
|
||||
char c = 1;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -658,7 +661,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintIntCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintIntCommits() throws Exception {
|
||||
int i = 1234;
|
||||
this.response.setContentLength(String.valueOf(i).length());
|
||||
|
||||
@@ -668,7 +671,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintLongCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintLongCommits() throws Exception {
|
||||
long l = 12345;
|
||||
this.response.setContentLength(String.valueOf(l).length());
|
||||
|
||||
@@ -678,7 +681,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintFloatCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintFloatCommits() throws Exception {
|
||||
float f = 12345;
|
||||
this.response.setContentLength(String.valueOf(f).length());
|
||||
|
||||
@@ -688,7 +691,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintDoubleCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintDoubleCommits() throws Exception {
|
||||
double x = 1.2345;
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -698,7 +701,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintCharArrayCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintCharArrayCommits() throws Exception {
|
||||
char[] x = new char[10];
|
||||
this.response.setContentLength(x.length);
|
||||
|
||||
@@ -708,7 +711,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintStringCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintStringCommits() throws Exception {
|
||||
String x = "12345";
|
||||
this.response.setContentLength(x.length());
|
||||
|
||||
@@ -718,7 +721,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintObjectCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintObjectCommits() throws Exception {
|
||||
Object x = "12345";
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -728,7 +731,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnCommits() throws Exception {
|
||||
this.response.setContentLength(NL.length());
|
||||
|
||||
this.response.getWriter().println();
|
||||
@@ -737,7 +740,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnBooleanCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnBooleanCommits() throws Exception {
|
||||
boolean b = true;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -747,7 +750,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnCharCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnCharCommits() throws Exception {
|
||||
char c = 1;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -757,7 +760,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnIntCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnIntCommits() throws Exception {
|
||||
int i = 12345;
|
||||
this.response.setContentLength(String.valueOf(i).length());
|
||||
|
||||
@@ -767,7 +770,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnLongCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnLongCommits() throws Exception {
|
||||
long l = 12345678;
|
||||
this.response.setContentLength(String.valueOf(l).length());
|
||||
|
||||
@@ -777,7 +780,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnFloatCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnFloatCommits() throws Exception {
|
||||
float f = 1234;
|
||||
this.response.setContentLength(String.valueOf(f).length());
|
||||
|
||||
@@ -787,7 +790,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnDoubleCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnDoubleCommits() throws Exception {
|
||||
double x = 1;
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -797,7 +800,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnCharArrayCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnCharArrayCommits() throws Exception {
|
||||
char[] x = new char[20];
|
||||
this.response.setContentLength(x.length);
|
||||
|
||||
@@ -807,9 +810,9 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnStringCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnStringCommits() throws Exception {
|
||||
String x = "1";
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
this.response.setContentLength(x.length());
|
||||
|
||||
this.response.getWriter().println(x);
|
||||
|
||||
@@ -817,7 +820,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterPrintlnObjectCommits() throws Exception {
|
||||
void contentLengthPrintWriterPrintlnObjectCommits() throws Exception {
|
||||
Object x = "1";
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -827,9 +830,9 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterAppendCharSequenceCommits() throws Exception {
|
||||
void contentLengthPrintWriterAppendCharSequenceCommits() throws Exception {
|
||||
String x = "a";
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
this.response.setContentLength(x.length());
|
||||
|
||||
this.response.getWriter().append(x);
|
||||
|
||||
@@ -837,8 +840,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterAppendCharSequenceIntIntCommits()
|
||||
throws Exception {
|
||||
void contentLengthPrintWriterAppendCharSequenceIntIntCommits() throws Exception {
|
||||
String x = "abcdef";
|
||||
int start = 1;
|
||||
int end = 3;
|
||||
@@ -850,7 +852,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPrintWriterAppendCharCommits() throws Exception {
|
||||
void contentLengthPrintWriterAppendCharCommits() throws Exception {
|
||||
char x = 1;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -860,7 +862,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamWriteIntCommits() throws Exception {
|
||||
void contentLengthOutputStreamWriteIntCommits() throws Exception {
|
||||
int expected = 1;
|
||||
this.response.setContentLength(String.valueOf(expected).length());
|
||||
|
||||
@@ -870,7 +872,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamWriteIntMultiDigitCommits() throws Exception {
|
||||
void contentLengthOutputStreamWriteIntMultiDigitCommits() throws Exception {
|
||||
int expected = 10000;
|
||||
this.response.setContentLength(String.valueOf(expected).length());
|
||||
|
||||
@@ -880,8 +882,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthPlus1OutputStreamWriteIntMultiDigitCommits()
|
||||
throws Exception {
|
||||
void contentLengthPlus1OutputStreamWriteIntMultiDigitCommits() throws Exception {
|
||||
int expected = 10000;
|
||||
this.response.setContentLength(String.valueOf(expected).length() + 1);
|
||||
|
||||
@@ -896,24 +897,23 @@ public class OnCommittedResponseWrapperTests {
|
||||
|
||||
// gh-171
|
||||
@Test
|
||||
public void contentLengthPlus1OutputStreamWriteByteArrayMultiDigitCommits()
|
||||
throws Exception {
|
||||
void contentLengthPlus1OutputStreamWriteByteArrayMultiDigitCommits() throws Exception {
|
||||
String expected = "{\n" + " \"parameterName\" : \"_csrf\",\n"
|
||||
+ " \"token\" : \"06300b65-c4aa-4c8f-8cda-39ee17f545a0\",\n"
|
||||
+ " \"headerName\" : \"X-CSRF-TOKEN\"\n" + "}";
|
||||
+ " \"token\" : \"06300b65-c4aa-4c8f-8cda-39ee17f545a0\",\n" + " \"headerName\" : \"X-CSRF-TOKEN\"\n"
|
||||
+ "}";
|
||||
this.response.setContentLength(expected.length() + 1);
|
||||
|
||||
this.response.getOutputStream().write(expected.getBytes());
|
||||
|
||||
assertThat(this.committed).isFalse();
|
||||
|
||||
this.response.getOutputStream().write("1".getBytes("UTF-8"));
|
||||
this.response.getOutputStream().write("1".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.committed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintBooleanCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintBooleanCommits() throws Exception {
|
||||
boolean b = true;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -923,7 +923,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintCharCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintCharCommits() throws Exception {
|
||||
char c = 1;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -933,7 +933,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintIntCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintIntCommits() throws Exception {
|
||||
int i = 1234;
|
||||
this.response.setContentLength(String.valueOf(i).length());
|
||||
|
||||
@@ -943,7 +943,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintLongCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintLongCommits() throws Exception {
|
||||
long l = 12345;
|
||||
this.response.setContentLength(String.valueOf(l).length());
|
||||
|
||||
@@ -953,7 +953,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintFloatCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintFloatCommits() throws Exception {
|
||||
float f = 12345;
|
||||
this.response.setContentLength(String.valueOf(f).length());
|
||||
|
||||
@@ -963,7 +963,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintDoubleCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintDoubleCommits() throws Exception {
|
||||
double x = 1.2345;
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -973,7 +973,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintStringCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintStringCommits() throws Exception {
|
||||
String x = "12345";
|
||||
this.response.setContentLength(x.length());
|
||||
|
||||
@@ -983,7 +983,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnCommits() throws Exception {
|
||||
this.response.setContentLength(NL.length());
|
||||
|
||||
this.response.getOutputStream().println();
|
||||
@@ -992,7 +992,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnBooleanCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnBooleanCommits() throws Exception {
|
||||
boolean b = true;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -1002,7 +1002,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnCharCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnCharCommits() throws Exception {
|
||||
char c = 1;
|
||||
this.response.setContentLength(1);
|
||||
|
||||
@@ -1012,7 +1012,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnIntCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnIntCommits() throws Exception {
|
||||
int i = 12345;
|
||||
this.response.setContentLength(String.valueOf(i).length());
|
||||
|
||||
@@ -1022,7 +1022,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnLongCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnLongCommits() throws Exception {
|
||||
long l = 12345678;
|
||||
this.response.setContentLength(String.valueOf(l).length());
|
||||
|
||||
@@ -1032,7 +1032,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnFloatCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnFloatCommits() throws Exception {
|
||||
float f = 1234;
|
||||
this.response.setContentLength(String.valueOf(f).length());
|
||||
|
||||
@@ -1042,7 +1042,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnDoubleCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnDoubleCommits() throws Exception {
|
||||
double x = 1;
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
|
||||
@@ -1052,9 +1052,9 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamPrintlnStringCommits() throws Exception {
|
||||
void contentLengthOutputStreamPrintlnStringCommits() throws Exception {
|
||||
String x = "1";
|
||||
this.response.setContentLength(String.valueOf(x).length());
|
||||
this.response.setContentLength(x.length());
|
||||
|
||||
this.response.getOutputStream().println(x);
|
||||
|
||||
@@ -1062,7 +1062,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthDoesNotCommit() throws IOException {
|
||||
void contentLengthDoesNotCommit() {
|
||||
String body = "something";
|
||||
|
||||
this.response.setContentLength(body.length());
|
||||
@@ -1071,7 +1071,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthOutputStreamWriteStringCommits() throws IOException {
|
||||
void contentLengthOutputStreamWriteStringCommits() throws IOException {
|
||||
String body = "something";
|
||||
this.response.setContentLength(body.length());
|
||||
|
||||
@@ -1081,10 +1081,9 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {
|
||||
void addHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {
|
||||
int expected = 1234;
|
||||
this.response.addHeader("Content-Length",
|
||||
String.valueOf(String.valueOf(expected).length()));
|
||||
this.response.addHeader("Content-Length", String.valueOf(String.valueOf(expected).length()));
|
||||
|
||||
this.response.getWriter().write(expected);
|
||||
|
||||
@@ -1092,7 +1091,7 @@ public class OnCommittedResponseWrapperTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bufferSizePrintWriterWriteCommits() throws Exception {
|
||||
void bufferSizePrintWriterWriteCommits() throws Exception {
|
||||
String expected = "1234567890";
|
||||
given(this.response.getBufferSize()).willReturn(expected.length());
|
||||
|
||||
@@ -1101,8 +1100,19 @@ public class OnCommittedResponseWrapperTests {
|
||||
assertThat(this.committed).isTrue();
|
||||
}
|
||||
|
||||
// gh-7261
|
||||
@Test
|
||||
public void bufferSizeCommitsOnce() throws Exception {
|
||||
void contentLengthLongOutputStreamWriteStringCommits() throws IOException {
|
||||
String body = "something";
|
||||
this.response.setContentLengthLong(body.length());
|
||||
|
||||
this.response.getOutputStream().print(body);
|
||||
|
||||
assertThat(this.committed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferSizeCommitsOnce() throws Exception {
|
||||
String expected = "1234567890";
|
||||
given(this.response.getBufferSize()).willReturn(expected.length());
|
||||
|
||||
@@ -1116,4 +1126,5 @@ public class OnCommittedResponseWrapperTests {
|
||||
|
||||
assertThat(this.committed).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
@@ -32,21 +33,27 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class OncePerRequestFilterTests {
|
||||
class OncePerRequestFilterTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private MockFilterChain chain;
|
||||
|
||||
private OncePerRequestFilter filter;
|
||||
|
||||
private HttpServlet servlet;
|
||||
|
||||
private List<OncePerRequestFilter> invocations;
|
||||
|
||||
@BeforeEach
|
||||
@SuppressWarnings("serial")
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.servlet = new HttpServlet() {
|
||||
};
|
||||
this.request = new MockHttpServletRequest();
|
||||
@@ -55,9 +62,8 @@ public class OncePerRequestFilterTests {
|
||||
this.invocations = new ArrayList<>();
|
||||
this.filter = new OncePerRequestFilter() {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
OncePerRequestFilterTests.this.invocations.add(this);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
@@ -65,34 +71,63 @@ public class OncePerRequestFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterOnce() throws ServletException, IOException {
|
||||
void doFilterOnce() throws ServletException, IOException {
|
||||
this.filter.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
assertThat(this.invocations).containsOnly(this.filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterMultiOnlyIvokesOnce() throws ServletException, IOException {
|
||||
this.filter.doFilter(this.request, this.response,
|
||||
new MockFilterChain(this.servlet, this.filter));
|
||||
void doFilterMultiOnlyIvokesOnce() throws ServletException, IOException {
|
||||
this.filter.doFilter(this.request, this.response, new MockFilterChain(this.servlet, this.filter));
|
||||
|
||||
assertThat(this.invocations).containsOnly(this.filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterOtherSubclassInvoked() throws ServletException, IOException {
|
||||
void doFilterOtherSubclassInvoked() throws ServletException, IOException {
|
||||
OncePerRequestFilter filter2 = new OncePerRequestFilter() {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
OncePerRequestFilterTests.this.invocations.add(this);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
};
|
||||
this.filter.doFilter(this.request, this.response,
|
||||
new MockFilterChain(this.servlet, filter2));
|
||||
this.filter.doFilter(this.request, this.response, new MockFilterChain(this.servlet, filter2));
|
||||
|
||||
assertThat(this.invocations).containsOnly(this.filter, filter2);
|
||||
}
|
||||
|
||||
@Test // gh-1470
|
||||
void filterNestedErrorDispatch() throws ServletException, IOException {
|
||||
TestOncePerRequestFilter filter = new TestOncePerRequestFilter();
|
||||
this.request.setAttribute(filter.getAlreadyFilteredAttributeName(), Boolean.TRUE);
|
||||
this.request.setDispatcherType(DispatcherType.ERROR);
|
||||
this.request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error");
|
||||
filter.doFilter(this.request, new MockHttpServletResponse(), this.chain);
|
||||
assertThat(filter.didFilter).isFalse();
|
||||
assertThat(filter.didFilterNestedErrorDispatch).isTrue();
|
||||
}
|
||||
|
||||
private static class TestOncePerRequestFilter extends OncePerRequestFilter {
|
||||
|
||||
private boolean didFilter;
|
||||
|
||||
private boolean didFilterNestedErrorDispatch;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) {
|
||||
this.didFilter = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterNestedErrorDispatch(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) {
|
||||
this.didFilterNestedErrorDispatch = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*/
|
||||
public class SessionEventHttpSessionListenerAdapterTests {
|
||||
class SessionEventHttpSessionListenerAdapterTests {
|
||||
|
||||
@Mock
|
||||
private HttpSessionListener listener1;
|
||||
@@ -68,10 +68,9 @@ public class SessionEventHttpSessionListenerAdapterTests {
|
||||
private SessionEventHttpSessionListenerAdapter listener;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(
|
||||
Arrays.asList(this.listener1, this.listener2));
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(Arrays.asList(this.listener1, this.listener2));
|
||||
this.listener.setServletContext(new MockServletContext());
|
||||
|
||||
Session session = new MapSession();
|
||||
@@ -83,7 +82,7 @@ public class SessionEventHttpSessionListenerAdapterTests {
|
||||
// make configuration easier (i.e. autowire all HttpSessionListeners and might get
|
||||
// none)
|
||||
@Test
|
||||
public void constructorEmptyWorks() {
|
||||
void constructorEmptyWorks() {
|
||||
new SessionEventHttpSessionListenerAdapter(Collections.emptyList());
|
||||
}
|
||||
|
||||
@@ -92,9 +91,8 @@ public class SessionEventHttpSessionListenerAdapterTests {
|
||||
* listeners
|
||||
*/
|
||||
@Test
|
||||
public void onApplicationEventEmptyListenersDoesNotUseEvent() {
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(
|
||||
Collections.emptyList());
|
||||
void onApplicationEventEmptyListenersDoesNotUseEvent() {
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(Collections.emptyList());
|
||||
this.destroyed = mock(SessionDestroyedEvent.class);
|
||||
|
||||
this.listener.onApplicationEvent(this.destroyed);
|
||||
@@ -103,25 +101,23 @@ public class SessionEventHttpSessionListenerAdapterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventDestroyed() {
|
||||
void onApplicationEventDestroyed() {
|
||||
this.listener.onApplicationEvent(this.destroyed);
|
||||
|
||||
verify(this.listener1).sessionDestroyed(this.sessionEvent.capture());
|
||||
verify(this.listener2).sessionDestroyed(this.sessionEvent.capture());
|
||||
|
||||
assertThat(this.sessionEvent.getValue().getSession().getId())
|
||||
.isEqualTo(this.destroyed.getSessionId());
|
||||
assertThat(this.sessionEvent.getValue().getSession().getId()).isEqualTo(this.destroyed.getSessionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventCreated() {
|
||||
void onApplicationEventCreated() {
|
||||
this.listener.onApplicationEvent(this.created);
|
||||
|
||||
verify(this.listener1).sessionCreated(this.sessionEvent.capture());
|
||||
verify(this.listener2).sessionCreated(this.sessionEvent.capture());
|
||||
|
||||
assertThat(this.sessionEvent.getValue().getSession().getId())
|
||||
.isEqualTo(this.created.getSessionId());
|
||||
assertThat(this.sessionEvent.getValue().getSession().getId()).isEqualTo(this.created.getSessionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ import static org.mockito.Mockito.verify;
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@Mock
|
||||
private ReactiveSessionRepository<S> sessionRepository;
|
||||
@@ -57,68 +57,62 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
private SpringSessionWebSessionStore<S> webSessionStore;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.webSessionStore = new SpringSessionWebSessionStore<>(this.sessionRepository);
|
||||
given(this.sessionRepository.findById(any()))
|
||||
.willReturn(Mono.just(this.findByIdSession));
|
||||
given(this.sessionRepository.createSession())
|
||||
.willReturn(Mono.just(this.createSession));
|
||||
given(this.sessionRepository.findById(any())).willReturn(Mono.just(this.findByIdSession));
|
||||
given(this.sessionRepository.createSession()).willReturn(Mono.just(this.createSession));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenNullRepositoryThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new SpringSessionWebSessionStore<S>(null))
|
||||
void constructorWhenNullRepositoryThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new SpringSessionWebSessionStore<S>(null))
|
||||
.withMessage("reactiveSessionRepository cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenNoAttributesThenNotStarted() {
|
||||
void createSessionWhenNoAttributesThenNotStarted() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
assertThat(createdWebSession.isStarted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenAddAttributeThenStarted() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
void createSessionWhenAddAttributeThenStarted() {
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
assertThat(createdWebSession.isStarted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndSizeThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndSizeThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.size()).isEqualTo(0);
|
||||
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
|
||||
assertThat(attributes.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndIsEmptyThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndIsEmptyThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.isEmpty()).isTrue();
|
||||
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
|
||||
assertThat(attributes.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndNotStringThenFalse() {
|
||||
void createSessionWhenGetAttributesAndContainsKeyAndNotStringThenFalse() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -127,7 +121,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndNotFoundThenFalse() {
|
||||
void createSessionWhenGetAttributesAndContainsKeyAndNotFoundThenFalse() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -136,9 +130,8 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndFoundThenTrue() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
void createSessionWhenGetAttributesAndContainsKeyAndFoundThenTrue() {
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -147,7 +140,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndPutThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -157,7 +150,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutNullThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndPutNullThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -167,7 +160,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndRemoveThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndRemoveThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -177,7 +170,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutAllThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndPutAllThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -187,9 +180,8 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndClearThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
void createSessionWhenGetAttributesAndClearThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -199,9 +191,8 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndKeySetThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
void createSessionWhenGetAttributesAndKeySetThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
@@ -210,9 +201,8 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndValuesThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
void createSessionWhenGetAttributesAndValuesThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton("a"));
|
||||
given(this.createSession.getAttribute("a")).willReturn("b");
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
@@ -222,10 +212,9 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndEntrySetThenDelegatesToCreateSession() {
|
||||
void createSessionWhenGetAttributesAndEntrySetThenDelegatesToCreateSession() {
|
||||
String attrName = "attrName";
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton(attrName));
|
||||
given(this.createSession.getAttributeNames()).willReturn(Collections.singleton(attrName));
|
||||
String attrValue = "attrValue";
|
||||
given(this.createSession.getAttribute(attrName)).willReturn(attrValue);
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
@@ -233,12 +222,11 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
Set<Map.Entry<String, Object>> entries = attributes.entrySet();
|
||||
|
||||
assertThat(entries)
|
||||
.containsExactly(new AbstractMap.SimpleEntry<>(attrName, attrValue));
|
||||
assertThat(entries).containsExactly(new AbstractMap.SimpleEntry<>(attrName, attrValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveSessionThenStarted() {
|
||||
void retrieveSessionThenStarted() {
|
||||
String id = "id";
|
||||
WebSession retrievedWebSession = this.webSessionStore.retrieveSession(id).block();
|
||||
|
||||
@@ -247,7 +235,7 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSessionWhenInvokedThenSessionSaved() {
|
||||
void removeSessionWhenInvokedThenSessionSaved() {
|
||||
String sessionId = "session-id";
|
||||
given(this.sessionRepository.deleteById(sessionId)).willReturn(Mono.empty());
|
||||
|
||||
@@ -257,21 +245,20 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setClockWhenNullThenException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.webSessionStore.setClock(null))
|
||||
void setClockWhenNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.webSessionStore.setClock(null))
|
||||
.withMessage("clock cannot be null");
|
||||
}
|
||||
|
||||
@Test // gh-1114
|
||||
public void createSessionThenSessionIsNotExpired() {
|
||||
void createSessionThenSessionIsNotExpired() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
|
||||
assertThat(createdWebSession.isExpired()).isFalse();
|
||||
}
|
||||
|
||||
@Test // gh-1114
|
||||
public void invalidateSessionThenSessionIsExpired() {
|
||||
void invalidateSessionThenSessionIsExpired() {
|
||||
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
|
||||
given(createdWebSession.invalidate()).willReturn(Mono.empty());
|
||||
|
||||
|
||||
@@ -35,33 +35,36 @@ import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
|
||||
@Mock
|
||||
ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Mock
|
||||
WebSocketHandler delegate;
|
||||
|
||||
@Mock
|
||||
WebSocketSession session;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<SessionConnectEvent> event;
|
||||
|
||||
WebSocketConnectHandlerDecoratorFactory factory;
|
||||
private WebSocketConnectHandlerDecoratorFactory factory;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.factory = new WebSocketConnectHandlerDecoratorFactory(this.eventPublisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNullEventPublisher() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new WebSocketConnectHandlerDecoratorFactory(null))
|
||||
void constructorNullEventPublisher() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new WebSocketConnectHandlerDecoratorFactory(null))
|
||||
.withMessage("eventPublisher cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decorateAfterConnectionEstablished() throws Exception {
|
||||
void decorateAfterConnectionEstablished() throws Exception {
|
||||
WebSocketHandler decorated = this.factory.decorate(this.delegate);
|
||||
|
||||
decorated.afterConnectionEstablished(this.session);
|
||||
@@ -71,13 +74,14 @@ public class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decorateAfterConnectionEstablishedEventError() throws Exception {
|
||||
void decorateAfterConnectionEstablishedEventError() throws Exception {
|
||||
WebSocketHandler decorated = this.factory.decorate(this.delegate);
|
||||
willThrow(new IllegalStateException("Test throw on publishEvent"))
|
||||
.given(this.eventPublisher).publishEvent(any(ApplicationEvent.class));
|
||||
willThrow(new IllegalStateException("Test throw on publishEvent")).given(this.eventPublisher)
|
||||
.publishEvent(any(ApplicationEvent.class));
|
||||
|
||||
decorated.afterConnectionEstablished(this.session);
|
||||
|
||||
verify(this.eventPublisher).publishEvent(any(SessionConnectEvent.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class WebSocketRegistryListenerTests {
|
||||
class WebSocketRegistryListenerTests {
|
||||
|
||||
@Mock
|
||||
private WebSocketSession wsSession;
|
||||
@@ -73,7 +73,7 @@ public class WebSocketRegistryListenerTests {
|
||||
private WebSocketRegistryListener listener;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
String sessionId = "session-id";
|
||||
MapSession session = new MapSession(sessionId);
|
||||
@@ -96,14 +96,14 @@ public class WebSocketRegistryListenerTests {
|
||||
this.listener = new WebSocketRegistryListener();
|
||||
this.connect = new SessionConnectEvent(this.listener, this.wsSession);
|
||||
this.connect2 = new SessionConnectEvent(this.listener, this.wsSession2);
|
||||
this.disconnect = new SessionDisconnectEvent(this.listener, this.message,
|
||||
this.wsSession.getId(), CloseStatus.NORMAL);
|
||||
this.disconnect = new SessionDisconnectEvent(this.listener, this.message, this.wsSession.getId(),
|
||||
CloseStatus.NORMAL);
|
||||
this.deleted = new SessionDeletedEvent(this.listener, session);
|
||||
this.expired = new SessionExpiredEvent(this.listener, session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectSessionDeleted() throws Exception {
|
||||
void onApplicationEventConnectSessionDeleted() throws Exception {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
|
||||
this.listener.onApplicationEvent(this.deleted);
|
||||
@@ -112,7 +112,7 @@ public class WebSocketRegistryListenerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectSessionExpired() throws Exception {
|
||||
void onApplicationEventConnectSessionExpired() throws Exception {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
|
||||
this.listener.onApplicationEvent(this.expired);
|
||||
@@ -121,7 +121,7 @@ public class WebSocketRegistryListenerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectSessionDeletedNullPrincipal() throws Exception {
|
||||
void onApplicationEventConnectSessionDeletedNullPrincipal() throws Exception {
|
||||
given(this.wsSession.getPrincipal()).willReturn(null);
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
|
||||
@@ -131,7 +131,7 @@ public class WebSocketRegistryListenerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectDisconnect() throws Exception {
|
||||
void onApplicationEventConnectDisconnect() throws Exception {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
this.listener.onApplicationEvent(this.disconnect);
|
||||
|
||||
@@ -143,7 +143,7 @@ public class WebSocketRegistryListenerTests {
|
||||
// gh-76
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onApplicationEventConnectDisconnectCleanup() {
|
||||
void onApplicationEventConnectDisconnectCleanup() {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
|
||||
this.listener.onApplicationEvent(this.disconnect);
|
||||
@@ -154,7 +154,7 @@ public class WebSocketRegistryListenerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectDisconnectNullSession() throws Exception {
|
||||
void onApplicationEventConnectDisconnectNullSession() {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
this.attributes.clear();
|
||||
|
||||
@@ -164,7 +164,7 @@ public class WebSocketRegistryListenerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onApplicationEventConnectConnectDisonnect() throws Exception {
|
||||
void onApplicationEventConnectConnectDisonnect() throws Exception {
|
||||
this.listener.onApplicationEvent(this.connect);
|
||||
this.listener.onApplicationEvent(this.connect2);
|
||||
this.listener.onApplicationEvent(this.disconnect);
|
||||
|
||||
@@ -51,25 +51,27 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
public class SessionRepositoryMessageInterceptorTests {
|
||||
class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
@Mock
|
||||
SessionRepository<Session> sessionRepository;
|
||||
|
||||
@Mock
|
||||
MessageChannel channel;
|
||||
|
||||
@Mock
|
||||
Session session;
|
||||
|
||||
Message<?> createMessage;
|
||||
private Message<?> createMessage;
|
||||
|
||||
SimpMessageHeaderAccessor headers;
|
||||
private SimpMessageHeaderAccessor headers;
|
||||
|
||||
SessionRepositoryMessageInterceptor<Session> interceptor;
|
||||
private SessionRepositoryMessageInterceptor<Session> interceptor;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = new SessionRepositoryMessageInterceptor<>(
|
||||
this.sessionRepository);
|
||||
this.interceptor = new SessionRepositoryMessageInterceptor<>(this.sessionRepository);
|
||||
this.headers = SimpMessageHeaderAccessor.create();
|
||||
this.headers.setSessionId("session");
|
||||
this.headers.setSessionAttributes(new HashMap<>());
|
||||
@@ -80,123 +82,112 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendconstructorNullRepository() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new SessionRepositoryMessageInterceptor<>(null))
|
||||
void preSendconstructorNullRepository() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new SessionRepositoryMessageInterceptor<>(null))
|
||||
.withMessage("sessionRepository cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendNullMessage() {
|
||||
void preSendNullMessage() {
|
||||
assertThat(this.interceptor.preSend(null, this.channel)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendConnectAckDoesNotInvokeSessionRepository() {
|
||||
void preSendConnectAckDoesNotInvokeSessionRepository() {
|
||||
setMessageType(SimpMessageType.CONNECT_ACK);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendHeartbeatDoesNotInvokeSessionRepository() {
|
||||
void preSendHeartbeatDoesNotInvokeSessionRepository() {
|
||||
setMessageType(SimpMessageType.HEARTBEAT);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendDisconnectDoesNotInvokeSessionRepository() {
|
||||
void preSendDisconnectDoesNotInvokeSessionRepository() {
|
||||
setMessageType(SimpMessageType.DISCONNECT);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendOtherDoesNotInvokeSessionRepository() {
|
||||
void preSendOtherDoesNotInvokeSessionRepository() {
|
||||
setMessageType(SimpMessageType.OTHER);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMatchingMessageTypesNull() {
|
||||
void setMatchingMessageTypesNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.setMatchingMessageTypes(null))
|
||||
.withMessage("matchingMessageTypes cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setMatchingMessageTypesEmpty() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.interceptor.setMatchingMessageTypes(null))
|
||||
.isThrownBy(() -> this.interceptor.setMatchingMessageTypes(Collections.emptySet()))
|
||||
.withMessage("matchingMessageTypes cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMatchingMessageTypesEmpty() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> this.interceptor.setMatchingMessageTypes(Collections.emptySet()))
|
||||
.withMessage("matchingMessageTypes cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendSetMatchingMessageTypes() {
|
||||
void preSendSetMatchingMessageTypes() {
|
||||
this.interceptor.setMatchingMessageTypes(EnumSet.of(SimpMessageType.DISCONNECT));
|
||||
setMessageType(SimpMessageType.DISCONNECT);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verify(this.sessionRepository).findById(anyString());
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendConnectUpdatesLastUpdateTime() {
|
||||
void preSendConnectUpdatesLastUpdateTime() {
|
||||
setMessageType(SimpMessageType.CONNECT);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendMessageUpdatesLastUpdateTime() {
|
||||
void preSendMessageUpdatesLastUpdateTime() {
|
||||
setMessageType(SimpMessageType.MESSAGE);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendSubscribeUpdatesLastUpdateTime() {
|
||||
void preSendSubscribeUpdatesLastUpdateTime() {
|
||||
setMessageType(SimpMessageType.SUBSCRIBE);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendUnsubscribeUpdatesLastUpdateTime() {
|
||||
void preSendUnsubscribeUpdatesLastUpdateTime() {
|
||||
setMessageType(SimpMessageType.UNSUBSCRIBE);
|
||||
this.session.setLastAccessedTime(Instant.EPOCH);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
@@ -204,7 +195,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
// This will updated when SPR-12288 is resolved
|
||||
@Test
|
||||
public void preSendExpiredSession() {
|
||||
void preSendExpiredSession() {
|
||||
setSessionId("expired");
|
||||
|
||||
this.interceptor.preSend(createMessage(), this.channel);
|
||||
@@ -213,74 +204,67 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendNullSessionId() {
|
||||
void preSendNullSessionId() {
|
||||
setSessionId(null);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendNullSessionAttributes() {
|
||||
void preSendNullSessionAttributes() {
|
||||
this.headers.setSessionAttributes(null);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel)).isSameAs(this.createMessage);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandshakeNotServletServerHttpRequest() throws Exception {
|
||||
void beforeHandshakeNotServletServerHttpRequest() {
|
||||
assertThat(this.interceptor.beforeHandshake(null, null, null, null)).isTrue();
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandshakeNullSession() throws Exception {
|
||||
ServletServerHttpRequest request = new ServletServerHttpRequest(
|
||||
new MockHttpServletRequest());
|
||||
void beforeHandshakeNullSession() {
|
||||
ServletServerHttpRequest request = new ServletServerHttpRequest(new MockHttpServletRequest());
|
||||
assertThat(this.interceptor.beforeHandshake(request, null, null, null)).isTrue();
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeHandshakeSession() throws Exception {
|
||||
void beforeHandshakeSession() {
|
||||
MockHttpServletRequest httpRequest = new MockHttpServletRequest();
|
||||
HttpSession httpSession = httpRequest.getSession();
|
||||
ServletServerHttpRequest request = new ServletServerHttpRequest(httpRequest);
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
assertThat(this.interceptor.beforeHandshake(request, null, null, attributes))
|
||||
.isTrue();
|
||||
assertThat(this.interceptor.beforeHandshake(request, null, null, attributes)).isTrue();
|
||||
|
||||
assertThat(attributes.size()).isEqualTo(1);
|
||||
assertThat(SessionRepositoryMessageInterceptor.getSessionId(attributes))
|
||||
.isEqualTo(httpSession.getId());
|
||||
assertThat(SessionRepositoryMessageInterceptor.getSessionId(attributes)).isEqualTo(httpSession.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* At the moment there is no need for afterHandshake to do anything.
|
||||
*/
|
||||
@Test
|
||||
public void afterHandshakeDoesNothing() {
|
||||
void afterHandshakeDoesNothing() {
|
||||
this.interceptor.afterHandshake(null, null, null, null);
|
||||
|
||||
verifyZeroInteractions(this.sessionRepository);
|
||||
}
|
||||
|
||||
private void setSessionId(String id) {
|
||||
SessionRepositoryMessageInterceptor
|
||||
.setSessionId(this.headers.getSessionAttributes(), id);
|
||||
SessionRepositoryMessageInterceptor.setSessionId(this.headers.getSessionAttributes(), id);
|
||||
}
|
||||
|
||||
private Message<?> createMessage() {
|
||||
this.createMessage = MessageBuilder.createMessage("",
|
||||
this.headers.getMessageHeaders());
|
||||
this.createMessage = MessageBuilder.createMessage("", this.headers.getMessageHeaders());
|
||||
return this.createMessage;
|
||||
}
|
||||
|
||||
@@ -302,4 +286,5 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ description = "Spring Session Redis implementation"
|
||||
dependencies {
|
||||
compile project(':spring-session-core')
|
||||
compile ("org.springframework.data:spring-data-redis") {
|
||||
exclude group: "org.slf4j", module: 'slf4j-api'
|
||||
exclude group: "org.slf4j", module: 'jcl-over-slf4j'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,9 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
|
||||
public class SessionEventRegistry implements ApplicationListener<AbstractSessionEvent> {
|
||||
|
||||
private Map<String, AbstractSessionEvent> events = new HashMap<>();
|
||||
|
||||
private ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
@@ -48,14 +50,12 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends AbstractSessionEvent> E getEvent(String sessionId)
|
||||
throws InterruptedException {
|
||||
public <E extends AbstractSessionEvent> E getEvent(String sessionId) throws InterruptedException {
|
||||
return (E) waitForEvent(sessionId);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <E extends AbstractSessionEvent> E waitForEvent(String sessionId)
|
||||
throws InterruptedException {
|
||||
private <E extends AbstractSessionEvent> E waitForEvent(String sessionId) throws InterruptedException {
|
||||
Object lock = getLock(sessionId);
|
||||
synchronized (lock) {
|
||||
if (!this.events.containsKey(sessionId)) {
|
||||
@@ -68,4 +68,5 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
|
||||
private Object getLock(String sessionId) {
|
||||
return this.locks.computeIfAbsent(sessionId, (k) -> new Object());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,20 +23,19 @@ import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
|
||||
/**
|
||||
* Base class for {@link RedisOperationsSessionRepository} integration tests.
|
||||
* Base class for Redis integration tests.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public abstract class AbstractRedisITests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.5";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.6";
|
||||
|
||||
protected static class BaseConfig {
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
.withExposedPorts(6379);
|
||||
GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE).withExposedPorts(6379);
|
||||
redisContainer.start();
|
||||
return redisContainer;
|
||||
}
|
||||
@@ -44,8 +43,7 @@ public abstract class AbstractRedisITests {
|
||||
@Bean
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(
|
||||
redisContainer().getContainerIpAddress(),
|
||||
redisContainer().getFirstMappedPort());
|
||||
redisContainer().getContainerIpAddress(), redisContainer().getFirstMappedPort());
|
||||
return new LettuceConnectionFactory(configuration);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@@ -34,22 +35,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ReactiveRedisOperationsSessionRepository}.
|
||||
* Integration tests for {@link ReactiveRedisSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
class ReactiveRedisSessionRepositoryITests extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private ReactiveRedisOperationsSessionRepository repository;
|
||||
private ReactiveRedisSessionRepository repository;
|
||||
|
||||
@Test
|
||||
public void saves() throws InterruptedException {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void saves() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
|
||||
String expectedAttributeName = "a";
|
||||
String expectedAttributeValue = "b";
|
||||
@@ -70,9 +70,8 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
}
|
||||
|
||||
@Test // gh-1399
|
||||
public void saveMultipleTimes() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession session = this.repository
|
||||
.createSession().block();
|
||||
void saveMultipleTimes() {
|
||||
RedisSession session = this.repository.createSession().block();
|
||||
session.setAttribute("attribute1", "value1");
|
||||
Mono<Void> save1 = this.repository.save(session);
|
||||
session.setAttribute("attribute2", "value2");
|
||||
@@ -81,9 +80,8 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putAllOnSingleAttrDoesNotRemoveOld() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void putAllOnSingleAttrDoesNotRemoveOld() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
toSave.setAttribute("a", "b");
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
@@ -103,17 +101,15 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenOnlyChangeId() throws Exception {
|
||||
void changeSessionIdWhenOnlyChangeId() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
toSave.setAttribute(attrName, attrValue);
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession findById = this.repository
|
||||
.findById(toSave.getId()).block();
|
||||
RedisSession findById = this.repository.findById(toSave.getId()).block();
|
||||
|
||||
assertThat(findById.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
|
||||
@@ -124,17 +120,14 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
|
||||
assertThat(this.repository.findById(originalFindById).block()).isNull();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession findByChangeSessionId = this.repository
|
||||
.findById(changeSessionId).block();
|
||||
RedisSession findByChangeSessionId = this.repository.findById(changeSessionId).block();
|
||||
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName))
|
||||
.isEqualTo(attrValue);
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenChangeTwice() throws Exception {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void changeSessionIdWhenChangeTwice() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
@@ -150,17 +143,15 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSetAttributeOnChangedSession() throws Exception {
|
||||
void changeSessionIdWhenSetAttributeOnChangedSession() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession findById = this.repository
|
||||
.findById(toSave.getId()).block();
|
||||
RedisSession findById = this.repository.findById(toSave.getId()).block();
|
||||
|
||||
findById.setAttribute(attrName, attrValue);
|
||||
|
||||
@@ -171,17 +162,14 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
|
||||
assertThat(this.repository.findById(originalFindById).block()).isNull();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession findByChangeSessionId = this.repository
|
||||
.findById(changeSessionId).block();
|
||||
RedisSession findByChangeSessionId = this.repository.findById(changeSessionId).block();
|
||||
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName))
|
||||
.isEqualTo(attrValue);
|
||||
assertThat(findByChangeSessionId.<String>getAttribute(attrName)).isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenHasNotSaved() throws Exception {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void changeSessionIdWhenHasNotSaved() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
|
||||
@@ -193,9 +181,8 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
|
||||
// gh-954
|
||||
@Test
|
||||
public void changeSessionIdSaveTwice() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void changeSessionIdSaveTwice() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
|
||||
@@ -208,23 +195,20 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
|
||||
// gh-1111
|
||||
@Test
|
||||
public void changeSessionSaveOldSessionInstance() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
void changeSessionSaveOldSessionInstance() {
|
||||
RedisSession toSave = this.repository.createSession().block();
|
||||
String sessionId = toSave.getId();
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession session = this.repository
|
||||
.findById(sessionId).block();
|
||||
RedisSession session = this.repository.findById(sessionId).block();
|
||||
session.changeSessionId();
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
this.repository.save(session).block();
|
||||
|
||||
toSave.setLastAccessedTime(Instant.now());
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.repository.save(toSave).block())
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.repository.save(toSave).block())
|
||||
.withMessage("Session was invalidated");
|
||||
|
||||
assertThat(this.repository.findById(sessionId).block()).isNull();
|
||||
@@ -37,7 +37,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.SessionEventRegistry;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
@@ -48,17 +48,23 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link RedisIndexedSessionRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
class RedisIndexedSessionRepositoryITests extends AbstractRedisITests {
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private static final String INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
|
||||
|
||||
@Autowired
|
||||
private RedisOperationsSessionRepository repository;
|
||||
private RedisIndexedSessionRepository repository;
|
||||
|
||||
@Autowired
|
||||
private SessionEventRegistry registry;
|
||||
@@ -71,34 +77,31 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
private SecurityContext changedContext;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
if (this.registry != null) {
|
||||
this.registry.clear();
|
||||
}
|
||||
this.context = SecurityContextHolder.createEmptyContext();
|
||||
this.context.setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
|
||||
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
|
||||
this.context.setAuthentication(new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(), "na",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER")));
|
||||
|
||||
this.changedContext = SecurityContextHolder.createEmptyContext();
|
||||
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
|
||||
"changedContext-" + UUID.randomUUID(), "na",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER")));
|
||||
"changedContext-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saves() throws InterruptedException {
|
||||
void saves() throws InterruptedException {
|
||||
String username = "saves-" + System.currentTimeMillis();
|
||||
|
||||
String usernameSessionKey = "RedisOperationsSessionRepositoryITests:index:"
|
||||
+ INDEX_NAME + ":" + username;
|
||||
String usernameSessionKey = "RedisIndexedSessionRepositoryITests:index:" + INDEX_NAME + ":" + username;
|
||||
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String expectedAttributeName = "a";
|
||||
String expectedAttributeValue = "b";
|
||||
toSave.setAttribute(expectedAttributeName, expectedAttributeValue);
|
||||
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
|
||||
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username, "password",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
|
||||
toSaveContext.setAuthentication(toSaveToken);
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, toSaveContext);
|
||||
@@ -108,10 +111,8 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
this.repository.save(toSave);
|
||||
|
||||
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
|
||||
assertThat(this.registry.<SessionCreatedEvent>getEvent(toSave.getId()))
|
||||
.isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(this.redis.boundSetOps(usernameSessionKey).members())
|
||||
.contains(toSave.getId());
|
||||
assertThat(this.registry.<SessionCreatedEvent>getEvent(toSave.getId())).isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(this.redis.boundSetOps(usernameSessionKey).members()).contains(toSave.getId());
|
||||
|
||||
Session session = this.repository.findById(toSave.getId());
|
||||
|
||||
@@ -127,16 +128,14 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
assertThat(this.repository.findById(toSave.getId())).isNull();
|
||||
assertThat(this.registry.<SessionDestroyedEvent>getEvent(toSave.getId()))
|
||||
.isInstanceOf(SessionDestroyedEvent.class);
|
||||
assertThat(this.redis.boundSetOps(usernameSessionKey).members())
|
||||
.doesNotContain(toSave.getId());
|
||||
assertThat(this.redis.boundSetOps(usernameSessionKey).members()).doesNotContain(toSave.getId());
|
||||
|
||||
assertThat(this.registry.getEvent(toSave.getId()).getSession()
|
||||
.<String>getAttribute(expectedAttributeName))
|
||||
assertThat(this.registry.getEvent(toSave.getId()).getSession().<String>getAttribute(expectedAttributeName))
|
||||
.isEqualTo(expectedAttributeValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putAllOnSingleAttrDoesNotRemoveOld() {
|
||||
void putAllOnSingleAttrDoesNotRemoveOld() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute("a", "b");
|
||||
|
||||
@@ -157,15 +156,15 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalName() throws Exception {
|
||||
void findByPrincipalName() throws Exception {
|
||||
String principalName = "findByPrincipalName" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
@@ -173,7 +172,28 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
this.repository.deleteById(toSave.getId());
|
||||
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByPrincipalNameExpireRemovesIndex() {
|
||||
String principalName = "findByPrincipalNameExpireRemovesIndex" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
String body = "RedisIndexedSessionRepositoryITests:sessions:expires:" + toSave.getId();
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
@@ -181,34 +201,8 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
|
||||
String principalName = "findByPrincipalNameExpireRemovesIndex"
|
||||
+ UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
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));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChange"
|
||||
+ UUID.randomUUID();
|
||||
void findByPrincipalNameNoPrincipalNameChange() {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChange" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
|
||||
@@ -217,17 +211,16 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute("other", "value");
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
|
||||
+ UUID.randomUUID();
|
||||
void findByPrincipalNameNoPrincipalNameChangeReload() {
|
||||
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
|
||||
@@ -238,15 +231,15 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute("other", "value");
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedPrincipalName() throws Exception {
|
||||
void findByDeletedPrincipalName() {
|
||||
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
@@ -256,14 +249,14 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute(INDEX_NAME, null);
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedPrincipalName() throws Exception {
|
||||
void findByChangedPrincipalName() {
|
||||
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
@@ -274,19 +267,18 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute(INDEX_NAME, principalNameChanged);
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalNameChanged);
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedPrincipalNameReload() throws Exception {
|
||||
void findByDeletedPrincipalNameReload() {
|
||||
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(INDEX_NAME, principalName);
|
||||
@@ -297,14 +289,14 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
getSession.setAttribute(INDEX_NAME, null);
|
||||
this.repository.save(getSession);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedPrincipalNameReload() throws Exception {
|
||||
void findByChangedPrincipalNameReload() {
|
||||
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
@@ -317,26 +309,25 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
getSession.setAttribute(INDEX_NAME, principalNameChanged);
|
||||
this.repository.save(getSession);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalName);
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
principalNameChanged);
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBySecurityPrincipalName() throws Exception {
|
||||
void findBySecurityPrincipalName() throws Exception {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
@@ -344,7 +335,27 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
this.repository.deleteById(toSave.getId());
|
||||
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBySecurityPrincipalNameExpireRemovesIndex() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
String body = "RedisIndexedSessionRepositoryITests:sessions:expires:" + toSave.getId();
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
@@ -352,30 +363,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
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));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(0);
|
||||
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
|
||||
void findByPrincipalNameNoSecurityPrincipalNameChange() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -384,16 +372,15 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute("other", "value");
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
|
||||
throws Exception {
|
||||
void findByPrincipalNameNoSecurityPrincipalNameChangeReload() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -404,15 +391,15 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute("other", "value");
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedSecurityPrincipalName() throws Exception {
|
||||
void findByDeletedSecurityPrincipalName() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -421,14 +408,14 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, null);
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedSecurityPrincipalName() throws Exception {
|
||||
void findByChangedSecurityPrincipalName() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -437,19 +424,18 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
|
||||
this.repository.save(toSave);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getChangedSecurityName());
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
|
||||
void findByDeletedSecurityPrincipalNameReload() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -459,14 +445,14 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
getSession.setAttribute(INDEX_NAME, null);
|
||||
this.repository.save(getSession);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getChangedSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByChangedSecurityPrincipalNameReload() throws Exception {
|
||||
void findByChangedSecurityPrincipalNameReload() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
|
||||
|
||||
@@ -477,19 +463,18 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
getSession.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
|
||||
this.repository.save(getSession);
|
||||
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository
|
||||
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
|
||||
Map<String, RedisSession> findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getSecurityName());
|
||||
assertThat(findByPrincipalName).isEmpty();
|
||||
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
|
||||
getChangedSecurityName());
|
||||
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
|
||||
|
||||
assertThat(findByPrincipalName).hasSize(1);
|
||||
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenOnlyChangeId() throws Exception {
|
||||
void changeSessionIdWhenOnlyChangeId() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
@@ -514,7 +499,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenChangeTwice() throws Exception {
|
||||
void changeSessionIdWhenChangeTwice() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
|
||||
this.repository.save(toSave);
|
||||
@@ -531,7 +516,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSetAttributeOnChangedSession() throws Exception {
|
||||
void changeSessionIdWhenSetAttributeOnChangedSession() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
|
||||
@@ -556,7 +541,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenHasNotSaved() throws Exception {
|
||||
void changeSessionIdWhenHasNotSaved() {
|
||||
String attrName = "changeSessionId";
|
||||
String attrValue = "changeSessionId-value";
|
||||
|
||||
@@ -572,7 +557,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
// gh-962
|
||||
@Test
|
||||
public void changeSessionIdSaveTwice() {
|
||||
void changeSessionIdSaveTwice() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
@@ -586,7 +571,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
// gh-1137
|
||||
@Test
|
||||
public void changeSessionIdWhenSessionIsDeleted() {
|
||||
void changeSessionIdWhenSessionIsDeleted() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String sessionId = toSave.getId();
|
||||
this.repository.save(toSave);
|
||||
@@ -601,7 +586,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Test // gh-1270
|
||||
public void changeSessionIdSaveConcurrently() {
|
||||
void changeSessionIdSaveConcurrently() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String originalId = toSave.getId();
|
||||
this.repository.save(toSave);
|
||||
@@ -628,11 +613,11 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(redisNamespace = "RedisOperationsSessionRepositoryITests")
|
||||
@EnableRedisHttpSession(redisNamespace = "RedisIndexedSessionRepositoryITests")
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
public SessionEventRegistry sessionEventRegistry() {
|
||||
SessionEventRegistry sessionEventRegistry() {
|
||||
return new SessionEventRegistry();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.data.redis.SimpleRedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.RedisSessionRepository.RedisSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@@ -41,25 +41,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link SimpleRedisOperationsSessionRepository}.
|
||||
* Integration tests for {@link RedisSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
class RedisSessionRepositoryITests extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private SimpleRedisOperationsSessionRepository sessionRepository;
|
||||
private RedisSessionRepository sessionRepository;
|
||||
|
||||
@Test
|
||||
void save_NewSession_ShouldSaveSession() {
|
||||
RedisSession session = createAndSaveSession(Instant.now());
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(
|
||||
Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
assertThat(session.getAttributeNames())
|
||||
.isEqualTo(Collections.singleton("attribute1"));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
assertThat(session.getAttributeNames()).isEqualTo(Collections.singleton("attribute1"));
|
||||
assertThat(session.<String>getAttribute("attribute1")).isEqualTo("value1");
|
||||
}
|
||||
|
||||
@@ -72,8 +71,7 @@ class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
void save_DeletedSession_ShouldThrowException() {
|
||||
RedisSession session = createAndSaveSession(Instant.now());
|
||||
this.sessionRepository.deleteById(session.getId());
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.sessionRepository.save(session))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.save(session))
|
||||
.withMessage("Session was invalidated");
|
||||
}
|
||||
|
||||
@@ -164,8 +162,7 @@ class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
this.sessionRepository.deleteById(originalSessionId);
|
||||
updateSession(session, Instant.now(), "attribute1", "value1");
|
||||
String newSessionId = session.changeSessionId();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.sessionRepository.save(session))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.save(session))
|
||||
.withMessage("Session was invalidated");
|
||||
assertThat(this.sessionRepository.findById(newSessionId)).isNull();
|
||||
assertThat(this.sessionRepository.findById(originalSessionId)).isNull();
|
||||
@@ -182,8 +179,7 @@ class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
this.sessionRepository.save(copy1);
|
||||
updateSession(copy2, now.plusSeconds(2L), "attribute3", "value3");
|
||||
String newSessionId2 = copy2.changeSessionId();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.sessionRepository.save(copy2))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.save(copy2))
|
||||
.withMessage("Session was invalidated");
|
||||
assertThat(this.sessionRepository.findById(newSessionId1)).isNotNull();
|
||||
assertThat(this.sessionRepository.findById(newSessionId2)).isNull();
|
||||
@@ -220,8 +216,8 @@ class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
return this.sessionRepository.findById(session.getId());
|
||||
}
|
||||
|
||||
private static void updateSession(RedisSession session, Instant lastAccessedTime,
|
||||
String attributeName, Object attributeValue) {
|
||||
private static void updateSession(RedisSession session, Instant lastAccessedTime, String attributeName,
|
||||
Object attributeValue) {
|
||||
session.setLastAccessedTime(lastAccessedTime);
|
||||
session.setAttribute(attributeName, attributeValue);
|
||||
}
|
||||
@@ -231,12 +227,11 @@ class SimpleRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
public SimpleRedisOperationsSessionRepository sessionRepository(
|
||||
RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisSessionRepository sessionRepository(RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return new SimpleRedisOperationsSessionRepository(redisTemplate);
|
||||
return new RedisSessionRepository(redisTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,8 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session>
|
||||
extends AbstractRedisITests {
|
||||
class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session> extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private SessionRepository<S> repository;
|
||||
@@ -56,16 +55,16 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session
|
||||
private final Object lock = new Object();
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
this.registry.setLock(this.lock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expireFiresSessionExpiredEvent() throws InterruptedException {
|
||||
void expireFiresSessionExpiredEvent() throws InterruptedException {
|
||||
S toSave = this.repository.createSession();
|
||||
toSave.setAttribute("a", "b");
|
||||
Authentication toSaveToken = new UsernamePasswordAuthenticationToken("user",
|
||||
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
Authentication toSaveToken = new UsernamePasswordAuthenticationToken("user", "password",
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
|
||||
toSaveContext.setAuthentication(toSaveToken);
|
||||
toSave.setAttribute("SPRING_SECURITY_CONTEXT", toSaveContext);
|
||||
@@ -89,9 +88,10 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session
|
||||
assertThat(this.registry.receivedEvent()).isTrue();
|
||||
}
|
||||
|
||||
static class SessionExpiredEventRegistry
|
||||
implements ApplicationListener<SessionExpiredEvent> {
|
||||
static class SessionExpiredEventRegistry implements ApplicationListener<SessionExpiredEvent> {
|
||||
|
||||
private boolean receivedEvent;
|
||||
|
||||
private Object lock;
|
||||
|
||||
@Override
|
||||
@@ -102,13 +102,14 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session
|
||||
}
|
||||
}
|
||||
|
||||
public boolean receivedEvent() {
|
||||
boolean receivedEvent() {
|
||||
return this.receivedEvent;
|
||||
}
|
||||
|
||||
public void setLock(Object lock) {
|
||||
void setLock(Object lock) {
|
||||
this.lock = lock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -116,7 +117,7 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
public SessionExpiredEventRegistry sessionDestroyedEventRegistry() {
|
||||
SessionExpiredEventRegistry sessionDestroyedEventRegistry() {
|
||||
return new SessionExpiredEventRegistry();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.data.redis.AbstractRedisITests;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@@ -35,14 +35,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisOperationsSessionRepositoryFlushImmediatelyITests<S extends Session>
|
||||
extends AbstractRedisITests {
|
||||
class RedisIndexedSessionRepositoryFlushImmediatelyITests<S extends Session> extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private SessionRepository<S> sessionRepository;
|
||||
|
||||
@Test
|
||||
public void savesOnCreate() throws InterruptedException {
|
||||
void savesOnCreate() {
|
||||
S created = this.sessionRepository.createSession();
|
||||
|
||||
S getSession = this.sessionRepository.findById(created.getId());
|
||||
@@ -51,7 +50,7 @@ public class RedisOperationsSessionRepositoryFlushImmediatelyITests<S extends Se
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE)
|
||||
@EnableRedisHttpSession(flushMode = FlushMode.IMMEDIATE)
|
||||
static class RedisHttpSessionConfig extends BaseConfig {
|
||||
|
||||
}
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITests {
|
||||
class RedisListenerContainerTaskExecutorITests extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private SessionTaskExecutor executor;
|
||||
@@ -54,10 +54,9 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
private RedisOperations<Object, Object> redis;
|
||||
|
||||
@Test
|
||||
public void testRedisDelEventsAreDispatchedInSessionTaskExecutor()
|
||||
throws InterruptedException {
|
||||
BoundSetOperations<Object, Object> ops = this.redis.boundSetOps(
|
||||
"spring:session:RedisListenerContainerTaskExecutorITests:expirations:dummy");
|
||||
void testRedisDelEventsAreDispatchedInSessionTaskExecutor() throws InterruptedException {
|
||||
BoundSetOperations<Object, Object> ops = this.redis
|
||||
.boundSetOps("spring:session:RedisListenerContainerTaskExecutorITests:expirations:dummy");
|
||||
ops.add("value");
|
||||
ops.remove("value");
|
||||
assertThat(this.executor.taskDispatched()).isTrue();
|
||||
@@ -65,7 +64,8 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
}
|
||||
|
||||
static class SessionTaskExecutor implements TaskExecutor {
|
||||
private Object lock = new Object();
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
}
|
||||
}
|
||||
|
||||
public boolean taskDispatched() throws InterruptedException {
|
||||
boolean taskDispatched() throws InterruptedException {
|
||||
if (this.taskDispatched != null) {
|
||||
return this.taskDispatched;
|
||||
}
|
||||
@@ -97,6 +97,7 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
}
|
||||
return (this.taskDispatched != null) ? this.taskDispatched : Boolean.FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -104,12 +105,12 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
public Executor springSessionRedisTaskExecutor() {
|
||||
Executor springSessionRedisTaskExecutor() {
|
||||
return new SessionTaskExecutor(Executors.newSingleThreadExecutor());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor springSessionRedisSubscriptionExecutor() {
|
||||
Executor springSessionRedisSubscriptionExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,321 +16,39 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} that is implemented using Spring Data's
|
||||
* {@link ReactiveRedisOperations}.
|
||||
* This {@link ReactiveSessionRepository} implementation is kept in order to support
|
||||
* migration to {@link ReactiveRedisSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link ReactiveRedisSessionRepository}
|
||||
*/
|
||||
public class ReactiveRedisOperationsSessionRepository implements
|
||||
ReactiveSessionRepository<ReactiveRedisOperationsSessionRepository.RedisSession> {
|
||||
@Deprecated
|
||||
public class ReactiveRedisOperationsSessionRepository extends ReactiveRedisSessionRepository {
|
||||
|
||||
/**
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
* Create a new {@link ReactiveRedisOperationsSessionRepository} instance.
|
||||
* @param sessionRedisOperations the {@link ReactiveRedisOperations} to use for
|
||||
* managing sessions
|
||||
* @see ReactiveRedisSessionRepository#ReactiveRedisSessionRepository(ReactiveRedisOperations)
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
private final ReactiveRedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
/**
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
|
||||
public ReactiveRedisOperationsSessionRepository(
|
||||
ReactiveRedisOperations<String, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
}
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
*
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
public ReactiveRedisOperationsSessionRepository(ReactiveRedisOperations<String, Object> sessionRedisOperations) {
|
||||
super(sessionRedisOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redis flush mode. Default flush mode is {@link RedisFlushMode#ON_SAVE}.
|
||||
*
|
||||
* @param redisFlushMode the new redis flush mode
|
||||
* @deprecated since 2.2.0 as support {@code IMMEDIATE} is removed
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
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(() -> {
|
||||
RedisSession session = new RedisSession();
|
||||
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
session.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
return Mono.just(session);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(RedisSession session) {
|
||||
if (session.isNew) {
|
||||
return session.save();
|
||||
}
|
||||
String sessionKey = getSessionKey(
|
||||
session.hasChangedSessionId() ? session.originalSessionId
|
||||
: session.getId());
|
||||
return this.sessionRedisOperations.hasKey(sessionKey).flatMap((exists) -> exists
|
||||
? session.save()
|
||||
: Mono.error(new IllegalStateException("Session was invalidated")));
|
||||
}
|
||||
|
||||
@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 RedisSessionMapper(id))
|
||||
.filter((session) -> !session.isExpired())
|
||||
.map(RedisSession::new)
|
||||
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteById(String id) {
|
||||
String sessionKey = getSessionKey(id);
|
||||
|
||||
return this.sessionRedisOperations.delete(sessionKey).then();
|
||||
}
|
||||
|
||||
private static String getAttributeKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
private String getSessionKey(String sessionId) {
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link RedisSession#saveDelta()} is invoked all the attributes that have been
|
||||
* changed will be persisted.
|
||||
*/
|
||||
final class RedisSession implements Session {
|
||||
|
||||
private final MapSession cached;
|
||||
|
||||
private final Map<String, Object> delta = new HashMap<>();
|
||||
|
||||
private boolean isNew;
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be
|
||||
* persisted in the next save operation.
|
||||
*/
|
||||
RedisSession() {
|
||||
this(new MapSession());
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY,
|
||||
getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
this.flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}.
|
||||
*
|
||||
* @param mapSession the {@link MapSession} that represents the persisted session
|
||||
* that was retrieved. Cannot be null.
|
||||
*/
|
||||
RedisSession(MapSession mapSession) {
|
||||
Assert.notNull(mapSession, "mapSession cannot be null");
|
||||
this.cached = mapSession;
|
||||
this.originalSessionId = mapSession.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.cached.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String changeSessionId() {
|
||||
return this.cached.changeSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNames() {
|
||||
return this.cached.getAttributeNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
putAndFlush(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.cached.removeAttribute(attributeName);
|
||||
putAndFlush(getAttributeKey(attributeName), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return this.cached.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
putAndFlush(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastAccessedTime() {
|
||||
return this.cached.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
putAndFlush(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.cached.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return this.cached.isExpired();
|
||||
}
|
||||
|
||||
private boolean hasChangedSessionId() {
|
||||
return !getId().equals(this.originalSessionId);
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
if (ReactiveRedisOperationsSessionRepository.this.redisFlushMode == RedisFlushMode.IMMEDIATE) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
private void putAndFlush(String a, Object v) {
|
||||
this.delta.put(a, v);
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
private Mono<Void> save() {
|
||||
return Mono.defer(() -> saveChangeSessionId().then(saveDelta())
|
||||
.doOnSuccess((aVoid) -> this.isNew = false));
|
||||
}
|
||||
|
||||
private Mono<Void> saveDelta() {
|
||||
if (this.delta.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionKey = getSessionKey(getId());
|
||||
Mono<Boolean> update = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.opsForHash().putAll(sessionKey, new HashMap<>(this.delta));
|
||||
Mono<Boolean> setTtl = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.expire(sessionKey, getMaxInactiveInterval());
|
||||
|
||||
return update.and(setTtl).and((s) -> {
|
||||
this.delta.clear();
|
||||
s.onComplete();
|
||||
}).then();
|
||||
}
|
||||
|
||||
private Mono<Void> saveChangeSessionId() {
|
||||
if (!hasChangedSessionId()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionId = getId();
|
||||
|
||||
Publisher<Void> replaceSessionId = (s) -> {
|
||||
this.originalSessionId = sessionId;
|
||||
s.onComplete();
|
||||
};
|
||||
|
||||
if (this.isNew) {
|
||||
return Mono.from(replaceSessionId);
|
||||
}
|
||||
else {
|
||||
String originalSessionKey = getSessionKey(this.originalSessionId);
|
||||
String sessionKey = getSessionKey(sessionId);
|
||||
|
||||
return ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalSessionKey, sessionKey).and(replaceSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} that is implemented using Spring Data's
|
||||
* {@link ReactiveRedisOperations}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class ReactiveRedisSessionRepository
|
||||
implements ReactiveSessionRepository<ReactiveRedisSessionRepository.RedisSession> {
|
||||
|
||||
/**
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
private final ReactiveRedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
/**
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveRedisSessionRepository} instance.
|
||||
* @param sessionRedisOperations the {@link ReactiveRedisOperations} to use for
|
||||
* managing sessions
|
||||
*/
|
||||
public ReactiveRedisSessionRepository(ReactiveRedisOperations<String, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
}
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ReactiveRedisOperations} used for sessions.
|
||||
* @return the {@link ReactiveRedisOperations} used for sessions
|
||||
*/
|
||||
public ReactiveRedisOperations<String, Object> getSessionRedisOperations() {
|
||||
return this.sessionRedisOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<RedisSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
return Mono.just(session);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(RedisSession session) {
|
||||
if (session.isNew) {
|
||||
return session.save();
|
||||
}
|
||||
String sessionKey = getSessionKey(session.hasChangedSessionId() ? session.originalSessionId : session.getId());
|
||||
return this.sessionRedisOperations.hasKey(sessionKey).flatMap(
|
||||
(exists) -> exists ? session.save() : Mono.error(new IllegalStateException("Session was invalidated")));
|
||||
}
|
||||
|
||||
@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 RedisSessionMapper(id))
|
||||
.filter((session) -> !session.isExpired())
|
||||
.map((session) -> new RedisSession(session, false))
|
||||
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteById(String id) {
|
||||
String sessionKey = getSessionKey(id);
|
||||
|
||||
return this.sessionRedisOperations.delete(sessionKey).then();
|
||||
}
|
||||
|
||||
private static String getAttributeKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
private String getSessionKey(String sessionId) {
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link RedisSession#saveDelta()} is invoked all the attributes that have been
|
||||
* changed will be persisted.
|
||||
*/
|
||||
final class RedisSession implements Session {
|
||||
|
||||
private final MapSession cached;
|
||||
|
||||
private final Map<String, Object> delta = new HashMap<>();
|
||||
|
||||
private boolean isNew;
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (ReactiveRedisSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getAttributeKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.cached.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String changeSessionId() {
|
||||
return this.cached.changeSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null
|
||||
&& ReactiveRedisSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNames() {
|
||||
return this.cached.getAttributeNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.cached.removeAttribute(attributeName);
|
||||
this.delta.put(getAttributeKey(attributeName), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return this.cached.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastAccessedTime() {
|
||||
return this.cached.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.cached.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return this.cached.isExpired();
|
||||
}
|
||||
|
||||
private boolean hasChangedSessionId() {
|
||||
return !getId().equals(this.originalSessionId);
|
||||
}
|
||||
|
||||
private Mono<Void> save() {
|
||||
return Mono.defer(() -> saveChangeSessionId().then(saveDelta()).doOnSuccess((aVoid) -> this.isNew = false));
|
||||
}
|
||||
|
||||
private Mono<Void> saveDelta() {
|
||||
if (this.delta.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionKey = getSessionKey(getId());
|
||||
Mono<Boolean> update = ReactiveRedisSessionRepository.this.sessionRedisOperations.opsForHash()
|
||||
.putAll(sessionKey, new HashMap<>(this.delta));
|
||||
Mono<Boolean> setTtl = ReactiveRedisSessionRepository.this.sessionRedisOperations.expire(sessionKey,
|
||||
getMaxInactiveInterval());
|
||||
|
||||
return update.and(setTtl).and((s) -> {
|
||||
this.delta.clear();
|
||||
s.onComplete();
|
||||
}).then();
|
||||
}
|
||||
|
||||
private Mono<Void> saveChangeSessionId() {
|
||||
if (!hasChangedSessionId()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionId = getId();
|
||||
|
||||
Publisher<Void> replaceSessionId = (s) -> {
|
||||
this.originalSessionId = sessionId;
|
||||
s.onComplete();
|
||||
};
|
||||
|
||||
if (this.isNew) {
|
||||
return Mono.from(replaceSessionId);
|
||||
}
|
||||
else {
|
||||
String originalSessionKey = getSessionKey(this.originalSessionId);
|
||||
String sessionKey = getSessionKey(sessionId);
|
||||
|
||||
return ReactiveRedisSessionRepository.this.sessionRedisOperations.rename(originalSessionKey, sessionKey)
|
||||
.and(replaceSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
/**
|
||||
@@ -23,14 +24,17 @@ import org.springframework.session.SessionRepository;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
* @deprecated since 2.2.0 in favor of {@link FlushMode}
|
||||
*/
|
||||
@Deprecated
|
||||
public enum RedisFlushMode {
|
||||
|
||||
/**
|
||||
* Only writes to Redis when
|
||||
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
|
||||
* a web environment this is typically done as soon as the HTTP response is committed.
|
||||
*/
|
||||
ON_SAVE,
|
||||
ON_SAVE(FlushMode.ON_SAVE),
|
||||
|
||||
/**
|
||||
* Writes to Redis as soon as possible. For example
|
||||
@@ -38,5 +42,16 @@ public enum RedisFlushMode {
|
||||
* example is that setting an attribute on the session will also write to Redis
|
||||
* immediately.
|
||||
*/
|
||||
IMMEDIATE
|
||||
IMMEDIATE(FlushMode.IMMEDIATE);
|
||||
|
||||
private final FlushMode flushMode;
|
||||
|
||||
RedisFlushMode(FlushMode flushMode) {
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public FlushMode getFlushMode() {
|
||||
return this.flushMode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,857 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
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;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.session.DelegatingIndexResolver;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.PrincipalNameIndexResolver;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@link org.springframework.session.SessionRepository} that is implemented using
|
||||
* Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web
|
||||
* environment, this is typically used in combination with {@link SessionRepositoryFilter}
|
||||
* . This implementation supports {@link SessionDeletedEvent} and
|
||||
* {@link SessionExpiredEvent} by implementing {@link MessageListener}.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Creating a new instance</h2>
|
||||
*
|
||||
* A typical example of how to create a new instance can be seen below:
|
||||
*
|
||||
* <pre>
|
||||
* RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
*
|
||||
* // ... configure redisTemplate ...
|
||||
*
|
||||
* RedisIndexedSessionRepository redisSessionRepository =
|
||||
* new RedisIndexedSessionRepository(redisTemplate);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For additional information on how to create a RedisTemplate, refer to the
|
||||
* <a href = "https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/"
|
||||
* > Spring Data Redis Reference</a>.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Storage Details</h2>
|
||||
*
|
||||
* The sections below outline how Redis is updated for each operation. An example of
|
||||
* creating a new session can be found below. The subsequent sections describe the
|
||||
* details.
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
|
||||
* EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
|
||||
* 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
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Saving a Session</h3>
|
||||
*
|
||||
* <p>
|
||||
* Each session is stored in Redis as a
|
||||
* <a href="https://redis.io/topics/data-types#hashes">Hash</a>. Each session is set and
|
||||
* updated using the <a href="https://redis.io/commands/hmset">HMSET command</a>. An
|
||||
* example of how each session is stored can be seen below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr:attrName2 someAttrValue2
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* In this example, the session following statements are true about the session:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe</li>
|
||||
* <li>The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970
|
||||
* GMT.</li>
|
||||
* <li>The session expires in 1800 seconds (30 minutes).</li>
|
||||
* <li>The session was last accessed at 1404360000000 in milliseconds since midnight of
|
||||
* 1/1/1970 GMT.</li>
|
||||
* <li>The session has two attributes. The first is "attrName" with the value of
|
||||
* "someAttrValue". The second session attribute is named "attrName2" with the value of
|
||||
* "someAttrValue2".</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <h3>Optimized Writes</h3>
|
||||
*
|
||||
* <p>
|
||||
* The {@link RedisIndexedSessionRepository.RedisSession} keeps track of the properties
|
||||
* that have changed and only updates those. This means if an attribute is written once
|
||||
* and read many times we only need to write that attribute once. For example, assume the
|
||||
* session attribute "sessionAttr2" from earlier was updated. The following would be
|
||||
* executed upon saving:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
|
||||
* </pre>
|
||||
*
|
||||
* <h3>SessionCreatedEvent</h3>
|
||||
*
|
||||
* <p>
|
||||
* When a session is created an event is sent to Redis with the channel of
|
||||
* "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such that
|
||||
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the sesion id. The body of the event will be
|
||||
* the session that was created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If registered as a {@link MessageListener}, then {@link RedisIndexedSessionRepository}
|
||||
* will then translate the Redis message into a {@link SessionCreatedEvent}.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Expiration</h3>
|
||||
*
|
||||
* <p>
|
||||
* An expiration is associated to each session using the
|
||||
* <a href="https://redis.io/commands/expire">EXPIRE command</a> based upon the
|
||||
* {@link RedisIndexedSessionRepository.RedisSession#getMaxInactiveInterval()} . For
|
||||
* example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* You will note that the expiration that is set is 5 minutes after the session actually
|
||||
* expires. This is necessary so that the value of the session can be accessed when the
|
||||
* session expires. An expiration is set on the session itself five minutes after it
|
||||
* actually expires to ensure it is cleaned up, but only after we perform any necessary
|
||||
* processing.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> The {@link #findById(String)} method ensures that no expired sessions will
|
||||
* be returned. This means there is no need to check the expiration before using a session
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Spring Session relies on the expired and delete
|
||||
* <a href="https://redis.io/topics/notifications">keyspace notifications</a> from Redis
|
||||
* to fire a SessionDestroyedEvent. It is the SessionDestroyedEvent that ensures resources
|
||||
* associated with the Session are cleaned up. For example, when using Spring Session's
|
||||
* WebSocket support the Redis expired or delete event is what triggers any WebSocket
|
||||
* connections associated with the session to be closed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Expiration is not tracked directly on the session key itself since this would mean the
|
||||
* session data would no longer be available. Instead a special session expires key is
|
||||
* used. In our example the expires key is:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
|
||||
* EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* When a session expires key is deleted or expires, the keyspace notification triggers a
|
||||
* lookup of the actual session and a {@link SessionDestroyedEvent} is fired.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* One problem with relying on Redis expiration exclusively is that Redis makes no
|
||||
* guarantee of when the expired event will be fired if the key has not been accessed.
|
||||
* Specifically the background task that Redis uses to clean up expired keys is a low
|
||||
* priority task and may not trigger the key expiration. For additional details see
|
||||
* <a href="https://redis.io/topics/notifications">Timing of expired events</a> section in
|
||||
* the Redis documentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To circumvent the fact that expired events are not guaranteed to happen we can ensure
|
||||
* that each key is accessed when it is expected to expire. This means that if the TTL is
|
||||
* expired on the key, Redis will remove the key and fire the expired event when we try to
|
||||
* access the key.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For this reason, each session expiration is also tracked to the nearest minute. This
|
||||
* allows a background task to access the potentially expired sessions to ensure that
|
||||
* Redis expired events are fired in a more deterministic fashion. For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
|
||||
* EXPIRE spring:session:expirations1439245080000 2100
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The background task will then use these mappings to explicitly request each session
|
||||
* expires key. By accessing the key, rather than deleting it, we ensure that Redis
|
||||
* deletes the key for us only if the TTL is expired.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>NOTE</b>: We do not explicitly delete the keys since in some instances there may be
|
||||
* a race condition that incorrectly identifies a key as expired when it is not. Short of
|
||||
* using distributed locks (which would kill our performance) there is no way to ensure
|
||||
* the consistency of the expiration mapping. By simply accessing the key, we ensure that
|
||||
* the key is only removed if the TTL on that key is expired.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class RedisIndexedSessionRepository
|
||||
implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RedisIndexedSessionRepository.class);
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
private int database = 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;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = (event) -> {
|
||||
};
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private IndexResolver<Session> indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
|
||||
|
||||
private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer();
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
|
||||
* sessions. Cannot be null.
|
||||
*/
|
||||
public RedisIndexedSessionRepository(RedisOperations<Object, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations, this::getExpirationsKey,
|
||||
this::getSessionKey);
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationEventPublisher} that is used to publish
|
||||
* {@link SessionDestroyedEvent}. The default is to not publish a
|
||||
* {@link SessionDestroyedEvent}.
|
||||
* @param applicationEventPublisher the {@link ApplicationEventPublisher} that is used
|
||||
* to publish {@link SessionDestroyedEvent}. Cannot be null.
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
Assert.notNull(applicationEventPublisher, "applicationEventPublisher cannot be null");
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link IndexResolver} to use.
|
||||
* @param indexResolver the index resolver
|
||||
*/
|
||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||
Assert.notNull(indexResolver, "indexResolver cannot be null");
|
||||
this.indexResolver = indexResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default redis serializer. Replaces default serializer which is based on
|
||||
* {@link JdkSerializationRedisSerializer}.
|
||||
* @param defaultSerializer the new default redis serializer
|
||||
*/
|
||||
public void setDefaultSerializer(RedisSerializer<Object> defaultSerializer) {
|
||||
Assert.notNull(defaultSerializer, "defaultSerializer cannot be null");
|
||||
this.defaultSerializer = defaultSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redis flush mode. Default flush mode is {@link FlushMode#ON_SAVE}.
|
||||
* @param flushMode the flush mode
|
||||
*/
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode cannot be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public RedisOperations<Object, Object> getSessionRedisOperations() {
|
||||
return this.sessionRedisOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(RedisSession session) {
|
||||
session.save();
|
||||
if (session.isNew) {
|
||||
String sessionCreatedKey = getSessionCreatedChannel(session.getId());
|
||||
this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
|
||||
session.isNew = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupExpiredSessions() {
|
||||
this.expirationPolicy.cleanExpiredSessions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession findById(String id) {
|
||||
return getSession(id, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RedisSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
|
||||
if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String principalKey = getPrincipalKey(indexValue);
|
||||
Set<Object> sessionIds = this.sessionRedisOperations.boundSetOps(principalKey).members();
|
||||
Map<String, RedisSession> sessions = new HashMap<>(sessionIds.size());
|
||||
for (Object id : sessionIds) {
|
||||
RedisSession session = findById((String) id);
|
||||
if (session != null) {
|
||||
sessions.put(session.getId(), session);
|
||||
}
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session.
|
||||
* @param id the session id
|
||||
* @param allowExpired if true, will also include expired sessions that have not been
|
||||
* deleted. If false, will ensure expired sessions are not returned.
|
||||
* @return the Redis session
|
||||
*/
|
||||
private RedisSession getSession(String id, boolean allowExpired) {
|
||||
Map<Object, Object> entries = getSessionBoundHashOperations(id).entries();
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
MapSession loaded = loadSession(id, entries);
|
||||
if (!allowExpired && loaded.isExpired()) {
|
||||
return null;
|
||||
}
|
||||
RedisSession result = new RedisSession(loaded, false);
|
||||
result.originalLastAccessTime = loaded.getLastAccessedTime();
|
||||
return result;
|
||||
}
|
||||
|
||||
private MapSession loadSession(String id, Map<Object, Object> entries) {
|
||||
MapSession loaded = new MapSession(id);
|
||||
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
if (RedisSessionMapper.CREATION_TIME_KEY.equals(key)) {
|
||||
loaded.setCreationTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY.equals(key)) {
|
||||
loaded.setMaxInactiveInterval(Duration.ofSeconds((int) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.LAST_ACCESSED_TIME_KEY.equals(key)) {
|
||||
loaded.setLastAccessedTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (key.startsWith(RedisSessionMapper.ATTRIBUTE_PREFIX)) {
|
||||
loaded.setAttribute(key.substring(RedisSessionMapper.ATTRIBUTE_PREFIX.length()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String sessionId) {
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanupPrincipalIndex(session);
|
||||
this.expirationPolicy.onDelete(session);
|
||||
|
||||
String expireKey = getExpiredKey(session.getId());
|
||||
this.sessionRedisOperations.delete(expireKey);
|
||||
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
save(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
session.flushImmediateIfNecessary();
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
byte[] messageChannel = message.getChannel();
|
||||
byte[] messageBody = message.getBody();
|
||||
|
||||
String channel = new String(messageChannel);
|
||||
|
||||
if (channel.startsWith(this.sessionCreatedChannelPrefix)) {
|
||||
// TODO: is this thread safe?
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer.deserialize(message.getBody());
|
||||
handleCreated(loaded, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
String body = new String(messageBody);
|
||||
if (!body.startsWith(getExpiredKeyPrefix())) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isDeleted = channel.equals(this.sessionDeletedChannel);
|
||||
if (isDeleted || channel.equals(this.sessionExpiredChannel)) {
|
||||
int beginIndex = body.lastIndexOf(":") + 1;
|
||||
int endIndex = body.length();
|
||||
String sessionId = body.substring(beginIndex, endIndex);
|
||||
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
|
||||
if (session == null) {
|
||||
logger.warn("Unable to publish SessionDestroyedEvent for session " + sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Publishing SessionDestroyedEvent for session " + sessionId);
|
||||
}
|
||||
|
||||
cleanupPrincipalIndex(session);
|
||||
|
||||
if (isDeleted) {
|
||||
handleDeleted(session);
|
||||
}
|
||||
else {
|
||||
handleExpired(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupPrincipalIndex(RedisSession session) {
|
||||
String sessionId = session.getId();
|
||||
Map<String, String> indexes = RedisIndexedSessionRepository.this.indexResolver.resolveIndexesFor(session);
|
||||
String principal = indexes.get(PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (principal != null) {
|
||||
this.sessionRedisOperations.boundSetOps(getPrincipalKey(principal)).remove(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreated(Map<Object, Object> loaded, String channel) {
|
||||
String id = channel.substring(channel.lastIndexOf(":") + 1);
|
||||
Session session = loadSession(id, loaded);
|
||||
publishEvent(new SessionCreatedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleDeleted(RedisSession session) {
|
||||
publishEvent(new SessionDeletedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleExpired(RedisSession session) {
|
||||
publishEvent(new SessionExpiredEvent(this, session));
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
try {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("Error publishing " + event + ".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Hash key for this session by prefixing it appropriately.
|
||||
* @param sessionId the session id
|
||||
* @return the Hash key for this session by prefixing it appropriately.
|
||||
*/
|
||||
String getSessionKey(String sessionId) {
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
String getPrincipalKey(String principalName) {
|
||||
return this.namespace + "index:" + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":"
|
||||
+ principalName;
|
||||
}
|
||||
|
||||
String getExpirationsKey(long expiration) {
|
||||
return this.namespace + "expirations:" + expiration;
|
||||
}
|
||||
|
||||
private String getExpiredKey(String sessionId) {
|
||||
return getExpiredKeyPrefix() + sessionId;
|
||||
}
|
||||
|
||||
private String getSessionCreatedChannel(String sessionId) {
|
||||
return getSessionCreatedChannelPrefix() + sessionId;
|
||||
}
|
||||
|
||||
private String getExpiredKeyPrefix() {
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BoundHashOperations} to operate on a {@link Session}.
|
||||
* @param sessionId the id of the {@link Session} to work with
|
||||
* @return the {@link BoundHashOperations} to operate on a {@link Session}
|
||||
*/
|
||||
private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(String sessionId) {
|
||||
String key = getSessionKey(sessionId);
|
||||
return this.sessionRedisOperations.boundHashOps(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key for the specified session attribute.
|
||||
* @param attributeName the attribute name
|
||||
* @return the attribute key name
|
||||
*/
|
||||
static String getSessionAttrNameKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link RedisIndexedSessionRepository.RedisSession#saveDelta()} is invoked all the
|
||||
* attributes that have been changed will be persisted.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
final class RedisSession implements Session {
|
||||
|
||||
private final MapSession cached;
|
||||
|
||||
private Instant originalLastAccessTime;
|
||||
|
||||
private Map<String, Object> delta = new HashMap<>();
|
||||
|
||||
private boolean isNew;
|
||||
|
||||
private String originalPrincipalName;
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
Map<String, String> indexes = RedisIndexedSessionRepository.this.indexResolver.resolveIndexesFor(this);
|
||||
this.originalPrincipalName = indexes.get(PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (RedisIndexedSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getSessionAttrNameKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return this.cached.isExpired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return this.cached.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.cached.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String changeSessionId() {
|
||||
return this.cached.changeSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastAccessedTime() {
|
||||
return this.cached.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.cached.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null
|
||||
&& RedisIndexedSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNames() {
|
||||
return this.cached.getAttributeNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.cached.removeAttribute(attributeName);
|
||||
this.delta.put(getSessionAttrNameKey(attributeName), null);
|
||||
flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
if (RedisIndexedSessionRepository.this.flushMode == FlushMode.IMMEDIATE) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
private void save() {
|
||||
saveChangeSessionId();
|
||||
saveDelta();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any attributes that have been changed and updates the expiration of this
|
||||
* session.
|
||||
*/
|
||||
private void saveDelta() {
|
||||
if (this.delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String sessionId = getId();
|
||||
getSessionBoundHashOperations(sessionId).putAll(this.delta);
|
||||
String principalSessionKey = getSessionAttrNameKey(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
String securityPrincipalSessionKey = getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);
|
||||
if (this.delta.containsKey(principalSessionKey) || this.delta.containsKey(securityPrincipalSessionKey)) {
|
||||
if (this.originalPrincipalName != null) {
|
||||
String originalPrincipalRedisKey = getPrincipalKey(this.originalPrincipalName);
|
||||
RedisIndexedSessionRepository.this.sessionRedisOperations.boundSetOps(originalPrincipalRedisKey)
|
||||
.remove(sessionId);
|
||||
}
|
||||
Map<String, String> indexes = RedisIndexedSessionRepository.this.indexResolver.resolveIndexesFor(this);
|
||||
String principal = indexes.get(PRINCIPAL_NAME_INDEX_NAME);
|
||||
this.originalPrincipalName = principal;
|
||||
if (principal != null) {
|
||||
String principalRedisKey = getPrincipalKey(principal);
|
||||
RedisIndexedSessionRepository.this.sessionRedisOperations.boundSetOps(principalRedisKey)
|
||||
.add(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
this.delta = new HashMap<>(this.delta.size());
|
||||
|
||||
Long originalExpiration = (this.originalLastAccessTime != null)
|
||||
? this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli() : null;
|
||||
RedisIndexedSessionRepository.this.expirationPolicy.onExpirationUpdated(originalExpiration, this);
|
||||
}
|
||||
|
||||
private void saveChangeSessionId() {
|
||||
String sessionId = getId();
|
||||
if (sessionId.equals(this.originalSessionId)) {
|
||||
return;
|
||||
}
|
||||
if (!this.isNew) {
|
||||
String originalSessionIdKey = getSessionKey(this.originalSessionId);
|
||||
String sessionIdKey = getSessionKey(sessionId);
|
||||
try {
|
||||
RedisIndexedSessionRepository.this.sessionRedisOperations.rename(originalSessionIdKey,
|
||||
sessionIdKey);
|
||||
}
|
||||
catch (NonTransientDataAccessException ex) {
|
||||
handleErrNoSuchKeyError(ex);
|
||||
}
|
||||
String originalExpiredKey = getExpiredKey(this.originalSessionId);
|
||||
String expiredKey = getExpiredKey(sessionId);
|
||||
try {
|
||||
RedisIndexedSessionRepository.this.sessionRedisOperations.rename(originalExpiredKey, expiredKey);
|
||||
}
|
||||
catch (NonTransientDataAccessException ex) {
|
||||
handleErrNoSuchKeyError(ex);
|
||||
}
|
||||
}
|
||||
this.originalSessionId = sessionId;
|
||||
}
|
||||
|
||||
private void handleErrNoSuchKeyError(NonTransientDataAccessException ex) {
|
||||
if (!"ERR no such key".equals(NestedExceptionUtils.getMostSpecificCause(ex).getMessage())) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,892 +16,42 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
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;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@link org.springframework.session.SessionRepository} that is implemented using
|
||||
* Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web
|
||||
* environment, this is typically used in combination with {@link SessionRepositoryFilter}
|
||||
* . This implementation supports {@link SessionDeletedEvent} and
|
||||
* {@link SessionExpiredEvent} by implementing {@link MessageListener}.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Creating a new instance</h2>
|
||||
*
|
||||
* A typical example of how to create a new instance can be seen below:
|
||||
*
|
||||
* <pre>
|
||||
* RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
*
|
||||
* // ... configure redisTemplate ...
|
||||
*
|
||||
* RedisOperationsSessionRepository redisSessionRepository =
|
||||
* new RedisOperationsSessionRepository(redisTemplate);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For additional information on how to create a RedisTemplate, refer to the
|
||||
* <a href = "https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/" >
|
||||
* Spring Data Redis Reference</a>.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Storage Details</h2>
|
||||
*
|
||||
* The sections below outline how Redis is updated for each operation. An example of
|
||||
* creating a new session can be found below. The subsequent sections describe the
|
||||
* details.
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
|
||||
* EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
|
||||
* 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
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Saving a Session</h3>
|
||||
*
|
||||
* <p>
|
||||
* Each session is stored in Redis as a
|
||||
* <a href="https://redis.io/topics/data-types#hashes">Hash</a>. Each session is set and
|
||||
* updated using the <a href="https://redis.io/commands/hmset">HMSET command</a>. An
|
||||
* example of how each session is stored can be seen below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr:attrName2 someAttrValue2
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* In this example, the session following statements are true about the session:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe</li>
|
||||
* <li>The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970
|
||||
* GMT.</li>
|
||||
* <li>The session expires in 1800 seconds (30 minutes).</li>
|
||||
* <li>The session was last accessed at 1404360000000 in milliseconds since midnight of
|
||||
* 1/1/1970 GMT.</li>
|
||||
* <li>The session has two attributes. The first is "attrName" with the value of
|
||||
* "someAttrValue". The second session attribute is named "attrName2" with the value of
|
||||
* "someAttrValue2".</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <h3>Optimized Writes</h3>
|
||||
*
|
||||
* <p>
|
||||
* The {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession} keeps track of the properties that have changed and only
|
||||
* updates those. This means if an attribute is written once and read many times we only
|
||||
* need to write that attribute once. For example, assume the session attribute
|
||||
* "sessionAttr2" from earlier was updated. The following would be executed upon saving:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
|
||||
* </pre>
|
||||
*
|
||||
* <h3>SessionCreatedEvent</h3>
|
||||
*
|
||||
* <p>
|
||||
* When a session is created an event is sent to Redis with the channel of
|
||||
* "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such that
|
||||
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the sesion id. The body of the event will be
|
||||
* the session that was created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If registered as a {@link MessageListener}, then
|
||||
* {@link RedisOperationsSessionRepository} will then translate the Redis message into a
|
||||
* {@link SessionCreatedEvent}.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Expiration</h3>
|
||||
*
|
||||
* <p>
|
||||
* An expiration is associated to each session using the
|
||||
* <a href="https://redis.io/commands/expire">EXPIRE command</a> based upon the
|
||||
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#getMaxInactiveInterval()}
|
||||
* . For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* You will note that the expiration that is set is 5 minutes after the session actually
|
||||
* expires. This is necessary so that the value of the session can be accessed when the
|
||||
* session expires. An expiration is set on the session itself five minutes after it
|
||||
* actually expires to ensure it is cleaned up, but only after we perform any necessary
|
||||
* processing.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> The {@link #findById(String)} method ensures that no expired sessions
|
||||
* will be returned. This means there is no need to check the expiration before using a
|
||||
* session
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Spring Session relies on the expired and delete
|
||||
* <a href="https://redis.io/topics/notifications">keyspace notifications</a> from Redis to
|
||||
* fire a SessionDestroyedEvent. It is the SessionDestroyedEvent that ensures resources
|
||||
* associated with the Session are cleaned up. For example, when using Spring Session's
|
||||
* WebSocket support the Redis expired or delete event is what triggers any WebSocket
|
||||
* connections associated with the session to be closed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Expiration is not tracked directly on the session key itself since this would mean the
|
||||
* session data would no longer be available. Instead a special session expires key is
|
||||
* used. In our example the expires key is:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
|
||||
* EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* When a session expires key is deleted or expires, the keyspace notification triggers a
|
||||
* lookup of the actual session and a {@link SessionDestroyedEvent} is fired.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* One problem with relying on Redis expiration exclusively is that Redis makes no
|
||||
* guarantee of when the expired event will be fired if the key has not been accessed.
|
||||
* Specifically the background task that Redis uses to clean up expired keys is a low
|
||||
* priority task and may not trigger the key expiration. For additional details see
|
||||
* <a href="https://redis.io/topics/notifications">Timing of expired events</a> section in
|
||||
* the Redis documentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* To circumvent the fact that expired events are not guaranteed to happen we can ensure
|
||||
* that each key is accessed when it is expected to expire. This means that if the TTL is
|
||||
* expired on the key, Redis will remove the key and fire the expired event when we try to
|
||||
* access the key.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For this reason, each session expiration is also tracked to the nearest minute. This
|
||||
* allows a background task to access the potentially expired sessions to ensure that
|
||||
* Redis expired events are fired in a more deterministic fashion. For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
|
||||
* EXPIRE spring:session:expirations1439245080000 2100
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The background task will then use these mappings to explicitly request each session
|
||||
* expires key. By accessing the key, rather than deleting it, we ensure that Redis
|
||||
* deletes the key for us only if the TTL is expired.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>NOTE</b>: We do not explicitly delete the keys since in some instances there may be
|
||||
* a race condition that incorrectly identifies a key as expired when it is not. Short of
|
||||
* using distributed locks (which would kill our performance) there is no way to ensure
|
||||
* the consistency of the expiration mapping. By simply accessing the key, we ensure that
|
||||
* the key is only removed if the TTL on that key is expired.
|
||||
* </p>
|
||||
* This {@link SessionRepository} implementation is kept in order to support migration to
|
||||
* {@link RedisIndexedSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link RedisIndexedSessionRepository}
|
||||
*/
|
||||
public class RedisOperationsSessionRepository implements
|
||||
FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>,
|
||||
MessageListener {
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(RedisOperationsSessionRepository.class);
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
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.
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
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;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() {
|
||||
@Override
|
||||
public void publishEvent(ApplicationEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishEvent(Object event) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer();
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
@Deprecated
|
||||
public class RedisOperationsSessionRepository extends RedisIndexedSessionRepository {
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
*
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
|
||||
* sessions. Cannot be null.
|
||||
* @see RedisIndexedSessionRepository#RedisIndexedSessionRepository(RedisOperations)
|
||||
*/
|
||||
public RedisOperationsSessionRepository(
|
||||
RedisOperations<Object, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations,
|
||||
this::getExpirationsKey, this::getSessionKey);
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationEventPublisher} that is used to publish
|
||||
* {@link SessionDestroyedEvent}. The default is to not publish a
|
||||
* {@link SessionDestroyedEvent}.
|
||||
*
|
||||
* @param applicationEventPublisher the {@link ApplicationEventPublisher} that is used
|
||||
* to publish {@link SessionDestroyedEvent}. Cannot be null.
|
||||
*/
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher applicationEventPublisher) {
|
||||
Assert.notNull(applicationEventPublisher,
|
||||
"applicationEventPublisher cannot be null");
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
*
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default redis serializer. Replaces default serializer which is based on
|
||||
* {@link JdkSerializationRedisSerializer}.
|
||||
*
|
||||
* @param defaultSerializer the new default redis serializer
|
||||
*/
|
||||
public void setDefaultSerializer(RedisSerializer<Object> defaultSerializer) {
|
||||
Assert.notNull(defaultSerializer, "defaultSerializer cannot be null");
|
||||
this.defaultSerializer = defaultSerializer;
|
||||
public RedisOperationsSessionRepository(RedisOperations<Object, Object> sessionRedisOperations) {
|
||||
super(sessionRedisOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redis flush mode. Default flush mode is {@link RedisFlushMode#ON_SAVE}.
|
||||
*
|
||||
* @param redisFlushMode the new redis flush mode
|
||||
* @deprecated since 2.2.0 in favor of {@link #setFlushMode(FlushMode)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
setFlushMode(redisFlushMode.getFlushMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(RedisSession session) {
|
||||
session.save();
|
||||
if (session.isNew()) {
|
||||
String sessionCreatedKey = getSessionCreatedChannel(session.getId());
|
||||
this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
|
||||
session.setNew(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupExpiredSessions() {
|
||||
this.expirationPolicy.cleanExpiredSessions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession findById(String id) {
|
||||
return getSession(id, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RedisSession> findByIndexNameAndIndexValue(String indexName,
|
||||
String indexValue) {
|
||||
if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String principalKey = getPrincipalKey(indexValue);
|
||||
Set<Object> sessionIds = this.sessionRedisOperations.boundSetOps(principalKey)
|
||||
.members();
|
||||
Map<String, RedisSession> sessions = new HashMap<>(
|
||||
sessionIds.size());
|
||||
for (Object id : sessionIds) {
|
||||
RedisSession session = findById((String) id);
|
||||
if (session != null) {
|
||||
sessions.put(session.getId(), session);
|
||||
}
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session.
|
||||
* @param id the session id
|
||||
* @param allowExpired if true, will also include expired sessions that have not been
|
||||
* deleted. If false, will ensure expired sessions are not returned.
|
||||
* @return the Redis session
|
||||
*/
|
||||
private RedisSession getSession(String id, boolean allowExpired) {
|
||||
Map<Object, Object> entries = getSessionBoundHashOperations(id).entries();
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
MapSession loaded = loadSession(id, entries);
|
||||
if (!allowExpired && loaded.isExpired()) {
|
||||
return null;
|
||||
}
|
||||
RedisSession result = new RedisSession(loaded);
|
||||
result.originalLastAccessTime = loaded.getLastAccessedTime();
|
||||
return result;
|
||||
}
|
||||
|
||||
private MapSession loadSession(String id, Map<Object, Object> entries) {
|
||||
MapSession loaded = new MapSession(id);
|
||||
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
if (RedisSessionMapper.CREATION_TIME_KEY.equals(key)) {
|
||||
loaded.setCreationTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY.equals(key)) {
|
||||
loaded.setMaxInactiveInterval(Duration.ofSeconds((int) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.LAST_ACCESSED_TIME_KEY.equals(key)) {
|
||||
loaded.setLastAccessedTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (key.startsWith(RedisSessionMapper.ATTRIBUTE_PREFIX)) {
|
||||
loaded.setAttribute(
|
||||
key.substring(RedisSessionMapper.ATTRIBUTE_PREFIX.length()),
|
||||
entry.getValue());
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String sessionId) {
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanupPrincipalIndex(session);
|
||||
this.expirationPolicy.onDelete(session);
|
||||
|
||||
String expireKey = getExpiredKey(session.getId());
|
||||
this.sessionRedisOperations.delete(expireKey);
|
||||
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
save(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
Duration maxInactiveInterval = Duration
|
||||
.ofSeconds((this.defaultMaxInactiveInterval != null)
|
||||
? this.defaultMaxInactiveInterval
|
||||
: MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
RedisSession session = new RedisSession(maxInactiveInterval);
|
||||
session.flushImmediateIfNecessary();
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
byte[] messageChannel = message.getChannel();
|
||||
byte[] messageBody = message.getBody();
|
||||
|
||||
String channel = new String(messageChannel);
|
||||
|
||||
if (channel.startsWith(this.sessionCreatedChannelPrefix)) {
|
||||
// TODO: is this thread safe?
|
||||
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer
|
||||
.deserialize(message.getBody());
|
||||
handleCreated(loaded, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
String body = new String(messageBody);
|
||||
if (!body.startsWith(getExpiredKeyPrefix())) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isDeleted = channel.equals(this.sessionDeletedChannel);
|
||||
if (isDeleted || channel.equals(this.sessionExpiredChannel)) {
|
||||
int beginIndex = body.lastIndexOf(":") + 1;
|
||||
int endIndex = body.length();
|
||||
String sessionId = body.substring(beginIndex, endIndex);
|
||||
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
|
||||
if (session == null) {
|
||||
logger.warn("Unable to publish SessionDestroyedEvent for session "
|
||||
+ sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Publishing SessionDestroyedEvent for session " + sessionId);
|
||||
}
|
||||
|
||||
cleanupPrincipalIndex(session);
|
||||
|
||||
if (isDeleted) {
|
||||
handleDeleted(session);
|
||||
}
|
||||
else {
|
||||
handleExpired(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupPrincipalIndex(RedisSession session) {
|
||||
String sessionId = session.getId();
|
||||
String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(session);
|
||||
if (principal != null) {
|
||||
this.sessionRedisOperations.boundSetOps(getPrincipalKey(principal))
|
||||
.remove(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreated(Map<Object, Object> loaded, String channel) {
|
||||
String id = channel.substring(channel.lastIndexOf(":") + 1);
|
||||
Session session = loadSession(id, loaded);
|
||||
publishEvent(new SessionCreatedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleDeleted(RedisSession session) {
|
||||
publishEvent(new SessionDeletedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleExpired(RedisSession session) {
|
||||
publishEvent(new SessionExpiredEvent(this, session));
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
try {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("Error publishing " + event + ".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Hash key for this session by prefixing it appropriately.
|
||||
*
|
||||
* @param sessionId the session id
|
||||
* @return the Hash key for this session by prefixing it appropriately.
|
||||
*/
|
||||
String getSessionKey(String sessionId) {
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
String getPrincipalKey(String principalName) {
|
||||
return this.namespace + "index:"
|
||||
+ FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":"
|
||||
+ principalName;
|
||||
}
|
||||
|
||||
String getExpirationsKey(long expiration) {
|
||||
return this.namespace + "expirations:" + expiration;
|
||||
}
|
||||
|
||||
private String getExpiredKey(String sessionId) {
|
||||
return getExpiredKeyPrefix() + sessionId;
|
||||
}
|
||||
|
||||
private String getSessionCreatedChannel(String sessionId) {
|
||||
return getSessionCreatedChannelPrefix() + sessionId;
|
||||
}
|
||||
|
||||
private String getExpiredKeyPrefix() {
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BoundHashOperations} to operate on a {@link Session}.
|
||||
* @param sessionId the id of the {@link Session} to work with
|
||||
* @return the {@link BoundHashOperations} to operate on a {@link Session}
|
||||
*/
|
||||
private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(
|
||||
String sessionId) {
|
||||
String key = getSessionKey(sessionId);
|
||||
return this.sessionRedisOperations.boundHashOps(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key for the specified session attribute.
|
||||
*
|
||||
* @param attributeName the attribute name
|
||||
* @return the attribute key name
|
||||
*/
|
||||
static String getSessionAttrNameKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()}
|
||||
* is invoked all the attributes that have been changed will be persisted.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
final class RedisSession implements Session {
|
||||
private final MapSession cached;
|
||||
private Instant originalLastAccessTime;
|
||||
private Map<String, Object> delta = new HashMap<>();
|
||||
private boolean isNew;
|
||||
private String originalPrincipalName;
|
||||
private String originalSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be
|
||||
* persisted in the next save operation.
|
||||
*
|
||||
* @param maxInactiveInterval the amount of time that the {@link Session} should
|
||||
* be kept alive between client requests.
|
||||
*/
|
||||
RedisSession(Duration maxInactiveInterval) {
|
||||
this(new MapSession());
|
||||
this.cached.setMaxInactiveInterval(maxInactiveInterval);
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY,
|
||||
getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}.
|
||||
*
|
||||
* @param cached the {@link MapSession} that represents the persisted session that
|
||||
* was retrieved. Cannot be null.
|
||||
*/
|
||||
RedisSession(MapSession cached) {
|
||||
Assert.notNull(cached, "MapSession cannot be null");
|
||||
this.cached = cached;
|
||||
this.originalPrincipalName = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
|
||||
this.originalSessionId = cached.getId();
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
this.putAndFlush(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return this.cached.isExpired();
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
return this.isNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return this.cached.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.cached.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String changeSessionId() {
|
||||
return this.cached.changeSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastAccessedTime() {
|
||||
return this.cached.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
this.putAndFlush(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.cached.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNames() {
|
||||
return this.cached.getAttributeNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
this.putAndFlush(getSessionAttrNameKey(attributeName), attributeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String attributeName) {
|
||||
this.cached.removeAttribute(attributeName);
|
||||
this.putAndFlush(getSessionAttrNameKey(attributeName), null);
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
if (RedisOperationsSessionRepository.this.redisFlushMode == RedisFlushMode.IMMEDIATE) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
private void putAndFlush(String a, Object v) {
|
||||
this.delta.put(a, v);
|
||||
this.flushImmediateIfNecessary();
|
||||
}
|
||||
|
||||
private void save() {
|
||||
saveChangeSessionId();
|
||||
saveDelta();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any attributes that have been changed and updates the expiration of this
|
||||
* session.
|
||||
*/
|
||||
private void saveDelta() {
|
||||
if (this.delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String sessionId = getId();
|
||||
getSessionBoundHashOperations(sessionId).putAll(this.delta);
|
||||
String principalSessionKey = getSessionAttrNameKey(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
String securityPrincipalSessionKey = getSessionAttrNameKey(
|
||||
SPRING_SECURITY_CONTEXT);
|
||||
if (this.delta.containsKey(principalSessionKey)
|
||||
|| this.delta.containsKey(securityPrincipalSessionKey)) {
|
||||
if (this.originalPrincipalName != null) {
|
||||
String originalPrincipalRedisKey = getPrincipalKey(
|
||||
this.originalPrincipalName);
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.boundSetOps(originalPrincipalRedisKey).remove(sessionId);
|
||||
}
|
||||
String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
|
||||
this.originalPrincipalName = principal;
|
||||
if (principal != null) {
|
||||
String principalRedisKey = getPrincipalKey(principal);
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.boundSetOps(principalRedisKey).add(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
this.delta = new HashMap<>(this.delta.size());
|
||||
|
||||
Long originalExpiration = (this.originalLastAccessTime != null)
|
||||
? this.originalLastAccessTime.plus(getMaxInactiveInterval())
|
||||
.toEpochMilli()
|
||||
: null;
|
||||
RedisOperationsSessionRepository.this.expirationPolicy
|
||||
.onExpirationUpdated(originalExpiration, this);
|
||||
}
|
||||
|
||||
private void saveChangeSessionId() {
|
||||
String sessionId = getId();
|
||||
if (sessionId.equals(this.originalSessionId)) {
|
||||
return;
|
||||
}
|
||||
if (!isNew()) {
|
||||
String originalSessionIdKey = getSessionKey(this.originalSessionId);
|
||||
String sessionIdKey = getSessionKey(sessionId);
|
||||
try {
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalSessionIdKey, sessionIdKey);
|
||||
}
|
||||
catch (NonTransientDataAccessException ex) {
|
||||
handleErrNoSuchKeyError(ex);
|
||||
}
|
||||
String originalExpiredKey = getExpiredKey(this.originalSessionId);
|
||||
String expiredKey = getExpiredKey(sessionId);
|
||||
try {
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalExpiredKey, expiredKey);
|
||||
}
|
||||
catch (NonTransientDataAccessException ex) {
|
||||
handleErrNoSuchKeyError(ex);
|
||||
}
|
||||
}
|
||||
this.originalSessionId = sessionId;
|
||||
}
|
||||
|
||||
private void handleErrNoSuchKeyError(NonTransientDataAccessException ex) {
|
||||
if (!"ERR no such key"
|
||||
.equals(NestedExceptionUtils.getMostSpecificCause(ex).getMessage())) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Principal name resolver helper class.
|
||||
*/
|
||||
static class PrincipalNameResolver {
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
public String resolvePrincipal(Session session) {
|
||||
String principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);
|
||||
if (principalName != null) {
|
||||
return principalName;
|
||||
}
|
||||
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (authentication != null) {
|
||||
Expression expression = this.parser
|
||||
.parseExpression("authentication?.name");
|
||||
return expression.getValue(authentication, String.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession;
|
||||
|
||||
/**
|
||||
* A strategy for expiring {@link RedisSession} instances. This performs two operations:
|
||||
@@ -48,11 +48,12 @@ import org.springframework.session.data.redis.RedisOperationsSessionRepository.R
|
||||
*/
|
||||
final class RedisSessionExpirationPolicy {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(RedisSessionExpirationPolicy.class);
|
||||
private static final Log logger = LogFactory.getLog(RedisSessionExpirationPolicy.class);
|
||||
|
||||
private final RedisOperations<Object, Object> redis;
|
||||
|
||||
private final Function<Long, String> lookupExpirationKey;
|
||||
|
||||
private final Function<String, String> lookupSessionKey;
|
||||
|
||||
RedisSessionExpirationPolicy(RedisOperations<Object, Object> sessionRedisOperations,
|
||||
@@ -63,13 +64,13 @@ final class RedisSessionExpirationPolicy {
|
||||
this.lookupSessionKey = lookupSessionKey;
|
||||
}
|
||||
|
||||
public void onDelete(Session session) {
|
||||
void onDelete(Session session) {
|
||||
long toExpire = roundUpToNextMinute(expiresInMillis(session));
|
||||
String expireKey = getExpirationKey(toExpire);
|
||||
this.redis.boundSetOps(expireKey).remove(session.getId());
|
||||
}
|
||||
|
||||
public void onExpirationUpdated(Long originalExpirationTimeInMilli, Session session) {
|
||||
void onExpirationUpdated(Long originalExpirationTimeInMilli, Session session) {
|
||||
String keyToExpire = "expires:" + session.getId();
|
||||
long toExpire = roundUpToNextMinute(expiresInMillis(session));
|
||||
|
||||
@@ -92,12 +93,10 @@ final class RedisSessionExpirationPolicy {
|
||||
}
|
||||
|
||||
String expireKey = getExpirationKey(toExpire);
|
||||
BoundSetOperations<Object, Object> expireOperations = this.redis
|
||||
.boundSetOps(expireKey);
|
||||
BoundSetOperations<Object, Object> expireOperations = this.redis.boundSetOps(expireKey);
|
||||
expireOperations.add(keyToExpire);
|
||||
|
||||
long fiveMinutesAfterExpires = sessionExpireInSeconds
|
||||
+ TimeUnit.MINUTES.toSeconds(5);
|
||||
long fiveMinutesAfterExpires = sessionExpireInSeconds + TimeUnit.MINUTES.toSeconds(5);
|
||||
|
||||
expireOperations.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
|
||||
if (sessionExpireInSeconds == 0) {
|
||||
@@ -105,11 +104,9 @@ final class RedisSessionExpirationPolicy {
|
||||
}
|
||||
else {
|
||||
this.redis.boundValueOps(sessionKey).append("");
|
||||
this.redis.boundValueOps(sessionKey).expire(sessionExpireInSeconds,
|
||||
TimeUnit.SECONDS);
|
||||
this.redis.boundValueOps(sessionKey).expire(sessionExpireInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
this.redis.boundHashOps(getSessionKey(session.getId()))
|
||||
.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
|
||||
this.redis.boundHashOps(getSessionKey(session.getId())).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
String getExpirationKey(long expires) {
|
||||
@@ -120,7 +117,7 @@ final class RedisSessionExpirationPolicy {
|
||||
return this.lookupSessionKey.apply(sessionId);
|
||||
}
|
||||
|
||||
public void cleanExpiredSessions() {
|
||||
void cleanExpiredSessions() {
|
||||
long now = System.currentTimeMillis();
|
||||
long prevMin = roundDownMinute(now);
|
||||
|
||||
@@ -141,7 +138,6 @@ final class RedisSessionExpirationPolicy {
|
||||
* By trying to access the session we only trigger a deletion if it the TTL is
|
||||
* expired. This is done to handle
|
||||
* https://github.com/spring-projects/spring-session/issues/93
|
||||
*
|
||||
* @param key the key
|
||||
*/
|
||||
private void touch(String key) {
|
||||
@@ -171,4 +167,5 @@ final class RedisSessionExpirationPolicy {
|
||||
date.clear(Calendar.MILLISECOND);
|
||||
return date.getTimeInMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -38,27 +40,26 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class SimpleRedisOperationsSessionRepository implements
|
||||
SessionRepository<SimpleRedisOperationsSessionRepository.RedisSession> {
|
||||
public class RedisSessionRepository implements SessionRepository<RedisSessionRepository.RedisSession> {
|
||||
|
||||
private static final String DEFAULT_KEY_NAMESPACE = "spring:session:";
|
||||
|
||||
private final RedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
private Duration defaultMaxInactiveInterval = Duration
|
||||
.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private String keyNamespace = DEFAULT_KEY_NAMESPACE;
|
||||
|
||||
private RedisFlushMode flushMode = RedisFlushMode.ON_SAVE;
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Create a new {@link SimpleRedisOperationsSessionRepository} instance.
|
||||
* Create a new {@link RedisSessionRepository} instance.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing
|
||||
* sessions
|
||||
* sessions
|
||||
*/
|
||||
public SimpleRedisOperationsSessionRepository(
|
||||
RedisOperations<String, Object> sessionRedisOperations) {
|
||||
public RedisSessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations mut not be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
}
|
||||
@@ -68,8 +69,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval,
|
||||
"defaultMaxInactiveInterval must not be null");
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
@@ -86,14 +86,25 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
* Set the flush mode.
|
||||
* @param flushMode the flush mode
|
||||
*/
|
||||
public void setFlushMode(RedisFlushMode flushMode) {
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode must not be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
*/
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
RedisSession session = new RedisSession(this.defaultMaxInactiveInterval);
|
||||
MapSession cached = new MapSession();
|
||||
cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
session.flushIfRequired();
|
||||
return session;
|
||||
}
|
||||
@@ -101,9 +112,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
@Override
|
||||
public void save(RedisSession session) {
|
||||
if (!session.isNew) {
|
||||
String key = getSessionKey(
|
||||
session.hasChangedSessionId() ? session.originalSessionId
|
||||
: session.getId());
|
||||
String key = getSessionKey(session.hasChangedSessionId() ? session.originalSessionId : session.getId());
|
||||
Boolean sessionExists = this.sessionRedisOperations.hasKey(key);
|
||||
if (sessionExists == null || !sessionExists) {
|
||||
throw new IllegalStateException("Session was invalidated");
|
||||
@@ -115,8 +124,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
@Override
|
||||
public RedisSession findById(String sessionId) {
|
||||
String key = getSessionKey(sessionId);
|
||||
Map<String, Object> entries = this.sessionRedisOperations
|
||||
.<String, Object>opsForHash().entries(key);
|
||||
Map<String, Object> entries = this.sessionRedisOperations.<String, Object>opsForHash().entries(key);
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -125,7 +133,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
deleteById(sessionId);
|
||||
return null;
|
||||
}
|
||||
return new RedisSession(session);
|
||||
return new RedisSession(session, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,6 +154,10 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
return this.keyNamespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
private static String getAttributeKey(String attributeName) {
|
||||
return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal {@link Session} implementation used by this {@link SessionRepository}.
|
||||
*/
|
||||
@@ -159,21 +171,20 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
|
||||
private String originalSessionId;
|
||||
|
||||
RedisSession(Duration maxInactiveInterval) {
|
||||
this(new MapSession());
|
||||
this.cached.setMaxInactiveInterval(maxInactiveInterval);
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY,
|
||||
getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
RedisSession(MapSession cached) {
|
||||
RedisSession(MapSession cached, boolean isNew) {
|
||||
this.cached = cached;
|
||||
this.isNew = isNew;
|
||||
this.originalSessionId = cached.getId();
|
||||
if (this.isNew) {
|
||||
this.delta.put(RedisSessionMapper.CREATION_TIME_KEY, cached.getCreationTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) cached.getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime().toEpochMilli());
|
||||
}
|
||||
if (this.isNew || (RedisSessionRepository.this.saveMode == SaveMode.ALWAYS)) {
|
||||
getAttributeNames().forEach((attributeName) -> this.delta.put(getAttributeKey(attributeName),
|
||||
cached.getAttribute(attributeName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,7 +199,11 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
return this.cached.getAttribute(attributeName);
|
||||
T attributeValue = this.cached.getAttribute(attributeName);
|
||||
if (attributeValue != null && RedisSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE)) {
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
}
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,8 +214,8 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
@Override
|
||||
public void setAttribute(String attributeName, Object attributeValue) {
|
||||
this.cached.setAttribute(attributeName, attributeValue);
|
||||
putAttribute(RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName,
|
||||
attributeValue);
|
||||
this.delta.put(getAttributeKey(attributeName), attributeValue);
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -216,8 +231,8 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
@Override
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.cached.setLastAccessedTime(lastAccessedTime);
|
||||
putAttribute(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
getLastAccessedTime().toEpochMilli());
|
||||
this.delta.put(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, getLastAccessedTime().toEpochMilli());
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,8 +243,8 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
@Override
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.cached.setMaxInactiveInterval(interval);
|
||||
putAttribute(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) getMaxInactiveInterval().getSeconds());
|
||||
this.delta.put(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) getMaxInactiveInterval().getSeconds());
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -243,7 +258,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private void flushIfRequired() {
|
||||
if (SimpleRedisOperationsSessionRepository.this.flushMode == RedisFlushMode.IMMEDIATE) {
|
||||
if (RedisSessionRepository.this.flushMode == FlushMode.IMMEDIATE) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
@@ -265,8 +280,7 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
if (!this.isNew) {
|
||||
String originalSessionIdKey = getSessionKey(this.originalSessionId);
|
||||
String sessionIdKey = getSessionKey(getId());
|
||||
SimpleRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalSessionIdKey, sessionIdKey);
|
||||
RedisSessionRepository.this.sessionRedisOperations.rename(originalSessionIdKey, sessionIdKey);
|
||||
}
|
||||
this.originalSessionId = getId();
|
||||
}
|
||||
@@ -277,21 +291,13 @@ public class SimpleRedisOperationsSessionRepository implements
|
||||
return;
|
||||
}
|
||||
String key = getSessionKey(getId());
|
||||
SimpleRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.opsForHash().putAll(key, new HashMap<>(this.delta));
|
||||
SimpleRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.expireAt(key,
|
||||
Date.from(Instant
|
||||
.ofEpochMilli(getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(getMaxInactiveInterval().getSeconds())));
|
||||
RedisSessionRepository.this.sessionRedisOperations.opsForHash().putAll(key, new HashMap<>(this.delta));
|
||||
RedisSessionRepository.this.sessionRedisOperations.expireAt(key,
|
||||
Date.from(Instant.ofEpochMilli(getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(getMaxInactiveInterval().getSeconds())));
|
||||
this.delta.clear();
|
||||
}
|
||||
|
||||
private void putAttribute(String name, Object value) {
|
||||
this.delta.put(name, value);
|
||||
flushIfRequired();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -81,7 +81,7 @@ public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction
|
||||
}
|
||||
catch (InvalidDataAccessApiUsageException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to configure Redis to keyspace notifications. See https://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent",
|
||||
"Unable to configure Redis to keyspace notifications. See https://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisindexedsessionrepository-sessiondestroyedevent",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,17 +24,16 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
|
||||
/**
|
||||
* Qualifier annotation for a {@link RedisConnectionFactory} to be injected in
|
||||
* {@link RedisOperationsSessionRepository}.
|
||||
* {@link RedisIndexedSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Qualifier
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,18 +23,21 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||
|
||||
/**
|
||||
* Annotation used to inject the Redis accessor used by Spring Session's Redis session
|
||||
* repository.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @see org.springframework.session.data.redis.RedisOperationsSessionRepository#getSessionRedisOperations()
|
||||
* @see org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository#getSessionRedisOperations()
|
||||
* @see RedisIndexedSessionRepository#getSessionRedisOperations()
|
||||
* @see RedisSessionRepository#getSessionRedisOperations()
|
||||
* @see ReactiveRedisSessionRepository#getSessionRedisOperations()
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Value("#{sessionRepository.sessionRedisOperations}")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,12 +25,14 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
/**
|
||||
@@ -63,7 +65,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisHttpSessionConfiguration.class)
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableRedisHttpSession {
|
||||
|
||||
/**
|
||||
@@ -83,7 +85,7 @@ public @interface EnableRedisHttpSession {
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
String redisNamespace() default RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
@@ -94,9 +96,23 @@ public @interface EnableRedisHttpSession {
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
* @since 1.1
|
||||
* @deprecated since 2.2.0 in favor of {@link #flushMode()}
|
||||
*/
|
||||
@Deprecated
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
|
||||
* In a web environment this happens just before the HTTP response is committed.
|
||||
* <p>
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link FlushMode} to use
|
||||
* @since 2.2.0
|
||||
*/
|
||||
FlushMode flushMode() default FlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* The cron expression for expired session cleanup job. By default runs every minute.
|
||||
* @return the session cleanup cron expression
|
||||
@@ -104,4 +120,12 @@ public @interface EnableRedisHttpSession {
|
||||
*/
|
||||
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
* @return the save mode
|
||||
* @since 2.2.0
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -48,10 +50,15 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
|
||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
@@ -73,18 +80,18 @@ import org.springframework.util.StringValueResolver;
|
||||
* @since 1.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableScheduling
|
||||
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
|
||||
SchedulingConfigurer {
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private String redisNamespace = RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
private String redisNamespace = RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
||||
|
||||
@@ -92,6 +99,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private IndexResolver<Session> indexResolver;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
@@ -100,33 +109,39 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private Executor redisSubscriptionExecutor;
|
||||
|
||||
private List<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
public RedisOperationsSessionRepository sessionRepository() {
|
||||
public RedisIndexedSessionRepository sessionRepository() {
|
||||
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
|
||||
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
|
||||
redisTemplate);
|
||||
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
|
||||
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
|
||||
if (this.indexResolver != null) {
|
||||
sessionRepository.setIndexResolver(this.indexResolver);
|
||||
}
|
||||
if (this.defaultRedisSerializer != null) {
|
||||
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
|
||||
}
|
||||
sessionRepository
|
||||
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setRedisFlushMode(this.redisFlushMode);
|
||||
sessionRepository.setFlushMode(this.flushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisMessageListenerContainer springSessionRedisMessageListenerContainer(
|
||||
RedisOperationsSessionRepository sessionRepository) {
|
||||
RedisIndexedSessionRepository sessionRepository) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(this.redisConnectionFactory);
|
||||
if (this.redisTaskExecutor != null) {
|
||||
@@ -136,19 +151,16 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
||||
}
|
||||
container.addMessageListener(sessionRepository,
|
||||
Arrays.asList(
|
||||
new ChannelTopic(sessionRepository.getSessionDeletedChannel()),
|
||||
Arrays.asList(new ChannelTopic(sessionRepository.getSessionDeletedChannel()),
|
||||
new ChannelTopic(sessionRepository.getSessionExpiredChannel())));
|
||||
container.addMessageListener(sessionRepository,
|
||||
Collections.singletonList(new PatternTopic(
|
||||
sessionRepository.getSessionCreatedChannelPrefix() + "*")));
|
||||
Collections.singletonList(new PatternTopic(sessionRepository.getSessionCreatedChannelPrefix() + "*")));
|
||||
return container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InitializingBean enableRedisKeyspaceNotificationsInitializer() {
|
||||
return new EnableRedisKeyspaceNotificationsInitializer(
|
||||
this.redisConnectionFactory, this.configureRedisAction);
|
||||
return new EnableRedisKeyspaceNotificationsInitializer(this.redisConnectionFactory, this.configureRedisAction);
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
@@ -159,9 +171,19 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
setFlushMode(redisFlushMode.getFlushMode());
|
||||
}
|
||||
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode cannot be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
public void setCleanupCron(String cleanupCron) {
|
||||
@@ -170,7 +192,6 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
/**
|
||||
* Sets the action to perform for configuring Redis.
|
||||
*
|
||||
* @param configureRedisAction the configureRedis to set. The default is
|
||||
* {@link ConfigureNotifyKeyspaceEventsAction}.
|
||||
*/
|
||||
@@ -183,8 +204,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
public void setRedisConnectionFactory(
|
||||
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||
ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
|
||||
RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory
|
||||
.getIfAvailable();
|
||||
RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory.getIfAvailable();
|
||||
if (redisConnectionFactoryToUse == null) {
|
||||
redisConnectionFactoryToUse = redisConnectionFactory.getObject();
|
||||
}
|
||||
@@ -193,17 +213,20 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(
|
||||
RedisSerializer<Object> defaultRedisSerializer) {
|
||||
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher applicationEventPublisher) {
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||
this.indexResolver = indexResolver;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionRedisTaskExecutor")
|
||||
public void setRedisTaskExecutor(Executor redisTaskExecutor) {
|
||||
@@ -216,6 +239,12 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
@@ -227,30 +256,29 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes
|
||||
.getNumber("maxInactiveIntervalInSeconds");
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver
|
||||
.resolveStringValue(redisNamespaceValue);
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
}
|
||||
this.redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
FlushMode flushMode = attributes.getEnum("flushMode");
|
||||
RedisFlushMode redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
if (flushMode == FlushMode.ON_SAVE && redisFlushMode != RedisFlushMode.ON_SAVE) {
|
||||
flushMode = redisFlushMode.getFlushMode();
|
||||
}
|
||||
this.flushMode = flushMode;
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
String cleanupCron = attributes.getString("cleanupCron");
|
||||
if (StringUtils.hasText(cleanupCron)) {
|
||||
this.cleanupCron = cleanupCron;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
taskRegistrar.addCronTask(() -> sessionRepository().cleanupExpiredSessions(),
|
||||
this.cleanupCron);
|
||||
}
|
||||
|
||||
private RedisTemplate<Object, Object> createRedisTemplate() {
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
@@ -273,7 +301,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
||||
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
}
|
||||
return RedisOperationsSessionRepository.DEFAULT_DATABASE;
|
||||
return RedisIndexedSessionRepository.DEFAULT_DATABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,15 +317,14 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private ConfigureRedisAction configure;
|
||||
|
||||
EnableRedisKeyspaceNotificationsInitializer(
|
||||
RedisConnectionFactory connectionFactory,
|
||||
EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory,
|
||||
ConfigureRedisAction configure) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.configure = configure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public void afterPropertiesSet() {
|
||||
if (this.configure == ConfigureRedisAction.NO_OP) {
|
||||
return;
|
||||
}
|
||||
@@ -310,12 +337,32 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
connection.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogFactory.getLog(getClass()).error("Error closing RedisConnection",
|
||||
ex);
|
||||
LogFactory.getLog(getClass()).error("Error closing RedisConnection", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration of scheduled job for cleaning up expired sessions.
|
||||
*/
|
||||
@EnableScheduling
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class SessionCleanupConfiguration implements SchedulingConfigurer {
|
||||
|
||||
private final RedisIndexedSessionRepository sessionRepository;
|
||||
|
||||
SessionCleanupConfiguration(RedisIndexedSessionRepository sessionRepository) {
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
taskRegistrar.addCronTask(this.sessionRepository::cleanupExpiredSessions,
|
||||
RedisHttpSessionConfiguration.this.cleanupCron);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,9 +27,10 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
@@ -62,7 +63,7 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisWebSessionConfiguration.class)
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableRedisWebSession {
|
||||
|
||||
/**
|
||||
@@ -82,7 +83,7 @@ public @interface EnableRedisWebSession {
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
String redisNamespace() default ReactiveRedisSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
@@ -93,7 +94,17 @@ public @interface EnableRedisWebSession {
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
* @deprecated since 2.2.0 as support {@code IMMEDIATE} is removed
|
||||
*/
|
||||
@Deprecated
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
* @return the save mode
|
||||
* @since 2.2.0
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -35,8 +37,10 @@ import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,29 +63,31 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private String redisNamespace = ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
private String redisNamespace = ReactiveRedisSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private ReactiveRedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private List<ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
public ReactiveRedisOperationsSessionRepository sessionRepository() {
|
||||
public ReactiveRedisSessionRepository sessionRepository() {
|
||||
ReactiveRedisTemplate<String, Object> reactiveRedisTemplate = createReactiveRedisTemplate();
|
||||
ReactiveRedisOperationsSessionRepository sessionRepository = new ReactiveRedisOperationsSessionRepository(
|
||||
reactiveRedisTemplate);
|
||||
sessionRepository
|
||||
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
ReactiveRedisSessionRepository sessionRepository = new ReactiveRedisSessionRepository(reactiveRedisTemplate);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setRedisFlushMode(this.redisFlushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
this.sessionRepositoryCustomizers
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -93,9 +99,13 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@@ -112,11 +122,16 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(
|
||||
RedisSerializer<Object> defaultRedisSerializer) {
|
||||
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
@@ -132,26 +147,22 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisWebSession.class.getName());
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes
|
||||
.getNumber("maxInactiveIntervalInSeconds");
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver
|
||||
.resolveStringValue(redisNamespaceValue);
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
}
|
||||
this.redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
}
|
||||
|
||||
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
|
||||
RedisSerializer<String> keySerializer = new StringRedisSerializer();
|
||||
RedisSerializer<Object> defaultSerializer = (this.defaultRedisSerializer != null)
|
||||
? this.defaultRedisSerializer
|
||||
RedisSerializer<Object> defaultSerializer = (this.defaultRedisSerializer != null) ? this.defaultRedisSerializer
|
||||
: new JdkSerializationRedisSerializer(this.classLoader);
|
||||
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
|
||||
.<String, Object>newSerializationContext(defaultSerializer)
|
||||
.key(keySerializer).hashKey(keySerializer).build();
|
||||
return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
|
||||
serializationContext);
|
||||
.<String, Object>newSerializationContext(defaultSerializer).key(keySerializer).hashKey(keySerializer)
|
||||
.build();
|
||||
return new ReactiveRedisTemplate<>(this.redisConnectionFactory, serializationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ import reactor.test.StepVerifier;
|
||||
import org.springframework.data.redis.core.ReactiveHashOperations;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository.RedisSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository.RedisSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -45,32 +46,28 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveRedisOperationsSessionRepository}.
|
||||
* Tests for {@link ReactiveRedisSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
class ReactiveRedisSessionRepositoryTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ReactiveRedisOperations<String, Object> redisOperations = mock(
|
||||
ReactiveRedisOperations.class);
|
||||
private ReactiveRedisOperations<String, Object> redisOperations = mock(ReactiveRedisOperations.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ReactiveHashOperations<String, Object, Object> hashOperations = mock(
|
||||
ReactiveHashOperations.class);
|
||||
private ReactiveHashOperations<String, Object, Object> hashOperations = mock(ReactiveHashOperations.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ArgumentCaptor<Map<String, Object>> delta = ArgumentCaptor
|
||||
.forClass(Map.class);
|
||||
private ArgumentCaptor<Map<String, Object>> delta = ArgumentCaptor.forClass(Map.class);
|
||||
|
||||
private ReactiveRedisOperationsSessionRepository repository;
|
||||
private ReactiveRedisSessionRepository repository;
|
||||
|
||||
private MapSession cached;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
this.repository = new ReactiveRedisOperationsSessionRepository(
|
||||
this.redisOperations);
|
||||
void setUp() {
|
||||
this.repository = new ReactiveRedisSessionRepository(this.redisOperations);
|
||||
|
||||
this.cached = new MapSession();
|
||||
this.cached.setId("session-id");
|
||||
@@ -79,84 +76,62 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWithNullReactiveRedisOperations() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ReactiveRedisOperationsSessionRepository(null))
|
||||
void constructorWithNullReactiveRedisOperations() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ReactiveRedisSessionRepository(null))
|
||||
.withMessageContaining("sessionRedisOperations cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customRedisKeyNamespace() {
|
||||
void customRedisKeyNamespace() {
|
||||
this.repository.setRedisKeyNamespace("test");
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "namespace"))
|
||||
.isEqualTo("test:");
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "namespace")).isEqualTo("test:");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullRedisKeyNamespace() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.repository.setRedisKeyNamespace(null))
|
||||
void nullRedisKeyNamespace() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setRedisKeyNamespace(null))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyRedisKeyNamespace() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.repository.setRedisKeyNamespace(""))
|
||||
void emptyRedisKeyNamespace() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setRedisKeyNamespace(""))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customMaxInactiveInterval() {
|
||||
void customMaxInactiveInterval() {
|
||||
this.repository.setDefaultMaxInactiveInterval(600);
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(this.repository,
|
||||
"defaultMaxInactiveInterval")).isEqualTo(600);
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "defaultMaxInactiveInterval")).isEqualTo(600);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customRedisFlushMode() {
|
||||
this.repository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "redisFlushMode"))
|
||||
.isEqualTo(RedisFlushMode.IMMEDIATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullRedisFlushMode() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.repository.setRedisFlushMode(null))
|
||||
.withMessage("redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionDefaultMaxInactiveInterval() {
|
||||
void createSessionDefaultMaxInactiveInterval() {
|
||||
StepVerifier.create(this.repository.createSession())
|
||||
.consumeNextWith((session) -> assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofSeconds(
|
||||
MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS)))
|
||||
.isEqualTo(Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS)))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionCustomMaxInactiveInterval() {
|
||||
void createSessionCustomMaxInactiveInterval() {
|
||||
this.repository.setDefaultMaxInactiveInterval(600);
|
||||
|
||||
StepVerifier.create(this.repository.createSession())
|
||||
.consumeNextWith((session) -> assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofSeconds(600)))
|
||||
.consumeNextWith(
|
||||
(session) -> assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(600)))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNewSession() {
|
||||
void saveNewSession() {
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
.willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession newSession = this.repository.new RedisSession();
|
||||
RedisSession newSession = this.repository.new RedisSession(new MapSession(), true);
|
||||
StepVerifier.create(this.repository.save(newSession)).verifyComplete();
|
||||
|
||||
verify(this.redisOperations).opsForHash();
|
||||
@@ -176,13 +151,11 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveSessionNothingChanged() {
|
||||
void saveSessionNothingChanged() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
.willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession session = this.repository.new RedisSession(
|
||||
new MapSession(this.cached));
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -192,14 +165,13 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveLastAccessChanged() {
|
||||
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()))
|
||||
.willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -210,21 +182,19 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
|
||||
assertThat(this.delta.getAllValues().get(0))
|
||||
.isEqualTo(map(RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
session.getLastAccessedTime().toEpochMilli()));
|
||||
assertThat(this.delta.getAllValues().get(0)).isEqualTo(
|
||||
map(RedisSessionMapper.LAST_ACCESSED_TIME_KEY, session.getLastAccessedTime().toEpochMilli()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveSetAttribute() {
|
||||
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()))
|
||||
.willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -236,20 +206,18 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
|
||||
assertThat(this.delta.getAllValues().get(0)).isEqualTo(
|
||||
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
|
||||
session.getAttribute(attrName)));
|
||||
map(RedisIndexedSessionRepository.getSessionAttrNameKey(attrName), session.getAttribute(attrName)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveRemoveAttribute() {
|
||||
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()))
|
||||
.willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true));
|
||||
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(new MapSession());
|
||||
RedisSession session = this.repository.new RedisSession(new MapSession(), false);
|
||||
session.removeAttribute(attrName);
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
@@ -260,14 +228,14 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
|
||||
assertThat(this.delta.getAllValues().get(0)).isEqualTo(map(
|
||||
RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), null));
|
||||
assertThat(this.delta.getAllValues().get(0))
|
||||
.isEqualTo(map(RedisIndexedSessionRepository.getSessionAttrNameKey(attrName), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisSessionGetAttributes() {
|
||||
void redisSessionGetAttributes() {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
@@ -278,7 +246,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
void delete() {
|
||||
given(this.redisOperations.delete(anyString())).willReturn(Mono.just(1L));
|
||||
|
||||
StepVerifier.create(this.repository.deleteById("test")).verifyComplete();
|
||||
@@ -289,7 +257,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionNotFound() {
|
||||
void getSessionNotFound() {
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.entries(anyString())).willReturn(Flux.empty());
|
||||
given(this.redisOperations.delete(anyString())).willReturn(Mono.just(0L));
|
||||
@@ -305,7 +273,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getSessionFound() {
|
||||
void getSessionFound() {
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
String attribute1 = "attribute1";
|
||||
String attribute2 = "attribute2";
|
||||
@@ -313,54 +281,38 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
expected.setLastAccessedTime(Instant.now().minusSeconds(60));
|
||||
expected.setAttribute(attribute1, "test");
|
||||
expected.setAttribute(attribute2, null);
|
||||
Map map = map(RedisSessionMapper.ATTRIBUTE_PREFIX + attribute1,
|
||||
expected.getAttribute(attribute1),
|
||||
RedisSessionMapper.ATTRIBUTE_PREFIX + attribute2,
|
||||
expected.getAttribute(attribute2), RedisSessionMapper.CREATION_TIME_KEY,
|
||||
expected.getCreationTime().toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) expected.getMaxInactiveInterval().getSeconds(),
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
expected.getLastAccessedTime().toEpochMilli());
|
||||
given(this.hashOperations.entries(anyString()))
|
||||
.willReturn(Flux.fromIterable(map.entrySet()));
|
||||
Map map = map(RedisSessionMapper.ATTRIBUTE_PREFIX + attribute1, expected.getAttribute(attribute1),
|
||||
RedisSessionMapper.ATTRIBUTE_PREFIX + attribute2, expected.getAttribute(attribute2),
|
||||
RedisSessionMapper.CREATION_TIME_KEY, expected.getCreationTime().toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) expected.getMaxInactiveInterval().getSeconds(),
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY, expected.getLastAccessedTime().toEpochMilli());
|
||||
given(this.hashOperations.entries(anyString())).willReturn(Flux.fromIterable(map.entrySet()));
|
||||
|
||||
StepVerifier.create(this.repository.findById("test"))
|
||||
.consumeNextWith((session) -> {
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).entries(anyString());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
StepVerifier.create(this.repository.findById("test")).consumeNextWith((session) -> {
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).entries(anyString());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
|
||||
assertThat(session.getId()).isEqualTo(expected.getId());
|
||||
assertThat(session.getAttributeNames())
|
||||
.isEqualTo(expected.getAttributeNames());
|
||||
assertThat(session.<String>getAttribute(attribute1))
|
||||
.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())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(
|
||||
session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
}).verifyComplete();
|
||||
assertThat(session.getId()).isEqualTo(expected.getId());
|
||||
assertThat(session.getAttributeNames()).isEqualTo(expected.getAttributeNames());
|
||||
assertThat(session.<String>getAttribute(attribute1)).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()).isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getSessionExpired() {
|
||||
void getSessionExpired() {
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
Map map = map(RedisSessionMapper.CREATION_TIME_KEY, 0L,
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1,
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
|
||||
given(this.hashOperations.entries(anyString()))
|
||||
.willReturn(Flux.fromIterable(map.entrySet()));
|
||||
Map map = map(RedisSessionMapper.CREATION_TIME_KEY, 0L, RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1,
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY, Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
|
||||
given(this.hashOperations.entries(anyString())).willReturn(Flux.fromIterable(map.entrySet()));
|
||||
given(this.redisOperations.delete(anyString())).willReturn(Mono.just(0L));
|
||||
|
||||
StepVerifier.create(this.repository.findById("test")).verifyComplete();
|
||||
@@ -373,8 +325,8 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test // gh-1120
|
||||
public void getAttributeNamesAndRemove() {
|
||||
RedisSession session = this.repository.new RedisSession(this.cached);
|
||||
void getAttributeNamesAndRemove() {
|
||||
RedisSession session = this.repository.new RedisSession(this.cached, false);
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
|
||||
@@ -385,6 +337,78 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnSetAttribute() {
|
||||
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())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(1);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnGetAttribute() {
|
||||
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())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(2);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeAlways() {
|
||||
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())).willReturn(Mono.just(true));
|
||||
this.repository.setSaveMode(SaveMode.ALWAYS);
|
||||
MapSession delegate = new MapSession();
|
||||
delegate.setAttribute("attribute1", "value1");
|
||||
delegate.setAttribute("attribute2", "value2");
|
||||
delegate.setAttribute("attribute3", "value3");
|
||||
RedisSession session = this.repository.new RedisSession(delegate, false);
|
||||
session.getAttribute("attribute2");
|
||||
session.setAttribute("attribute3", "value4");
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
assertThat(this.delta.getValue()).hasSize(3);
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
private Map<String, Object> map(Object... objects) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (objects == null) {
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user