Compare commits
416 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60e88e8113 | ||
|
|
e1cdaf6385 | ||
|
|
0c93235dc3 | ||
|
|
ab208e2bbb | ||
|
|
b760b1886d | ||
|
|
5d42040524 | ||
|
|
7c66afe2e1 | ||
|
|
62b5efe838 | ||
|
|
b79dc79dbc | ||
|
|
1ead9f744c | ||
|
|
7fd0739c20 | ||
|
|
352c234d21 | ||
|
|
617bd340d0 | ||
|
|
70fc4c1ede | ||
|
|
e44b99ac14 | ||
|
|
f6101b7e30 | ||
|
|
240c979679 | ||
|
|
508e3e90a7 | ||
|
|
0018a2f772 | ||
|
|
e9f097db5c | ||
|
|
7e41e40762 | ||
|
|
dc553104c6 | ||
|
|
2b4a349b1a | ||
|
|
4a1f132e07 | ||
|
|
940555653c | ||
|
|
82cb417910 | ||
|
|
808a550343 | ||
|
|
cc08f83ce0 | ||
|
|
53c81de5e3 | ||
|
|
dd268992a8 | ||
|
|
00004e2f50 | ||
|
|
afb9a11abf | ||
|
|
fb356f02ff | ||
|
|
509fb99a5d | ||
|
|
2aff5b0377 | ||
|
|
016a007d12 | ||
|
|
2a3ec2f4b9 | ||
|
|
458ac6e700 | ||
|
|
33321d2e53 | ||
|
|
20130f8e8a | ||
|
|
03f008f283 | ||
|
|
00e7110594 | ||
|
|
f973e63fce | ||
|
|
edbf8bf587 | ||
|
|
234f6c954a | ||
|
|
e0417523f6 | ||
|
|
5865b0a715 | ||
|
|
67201417df | ||
|
|
01a149737e | ||
|
|
2d6f505a30 | ||
|
|
7c616a1adf | ||
|
|
61b01d9ecd | ||
|
|
f9163d94fd | ||
|
|
3f819a94b1 | ||
|
|
1e1d24895c | ||
|
|
e134a4cdb8 | ||
|
|
75006cd7dd | ||
|
|
9c8f8894e1 | ||
|
|
99db45ea72 | ||
|
|
c07583bd47 | ||
|
|
cce8dac4b7 | ||
|
|
5bde226ecc | ||
|
|
f8f6ee20c0 | ||
|
|
3bb96e8e82 | ||
|
|
b7367680cb | ||
|
|
a26a21b663 | ||
|
|
3825a46418 | ||
|
|
779277b16d | ||
|
|
b97306a83d | ||
|
|
6b13111079 | ||
|
|
63006db45d | ||
|
|
0a99e065ff | ||
|
|
bd2d846917 | ||
|
|
79928bd7fe | ||
|
|
903cac492e | ||
|
|
3980be349b | ||
|
|
128e0c4d47 | ||
|
|
37bc6a352f | ||
|
|
b88f48f01d | ||
|
|
028e277fc9 | ||
|
|
904cdf4e65 | ||
|
|
b58ea03a3b | ||
|
|
9014ac9060 | ||
|
|
7a82915a98 | ||
|
|
e16224583c | ||
|
|
96861ea535 | ||
|
|
de3d04fb29 | ||
|
|
68823eec29 | ||
|
|
a58d4f64fb | ||
|
|
40ad82806c | ||
|
|
6db7d45f65 | ||
|
|
bbbdf939e8 | ||
|
|
e23398b890 | ||
|
|
5cfbeae161 | ||
|
|
c6e71a90bb | ||
|
|
195228ff5a | ||
|
|
831d2f4152 | ||
|
|
4f7728f5b5 | ||
|
|
11e418526e | ||
|
|
49f24e493d | ||
|
|
bd6d5bf419 | ||
|
|
9b989c4a3e | ||
|
|
68ddcef83e | ||
|
|
2592905b41 | ||
|
|
4be47f6b40 | ||
|
|
35b5fcdc75 | ||
|
|
4d010b23b8 | ||
|
|
f0200696ef | ||
|
|
7f3302253b | ||
|
|
9e3bcafa75 | ||
|
|
216bfd7355 | ||
|
|
34cebc3df6 | ||
|
|
7b28b214ff | ||
|
|
8f8cfe5d79 | ||
|
|
e0e29eab35 | ||
|
|
cd38e307e0 | ||
|
|
091d0d8d9f | ||
|
|
3300b58afe | ||
|
|
00af8456d4 | ||
|
|
aeb0add50e | ||
|
|
671bfa0444 | ||
|
|
8bf3a251bb | ||
|
|
5d1c60711f | ||
|
|
32cf8ad7ad | ||
|
|
d3139b04e1 | ||
|
|
cc0bfc2299 | ||
|
|
c0a5916413 | ||
|
|
11ca552625 | ||
|
|
8e4ed22974 | ||
|
|
53d7c84f73 | ||
|
|
1871915c62 | ||
|
|
d5259bf0e9 | ||
|
|
12b1dda24c | ||
|
|
fad66c5cd2 | ||
|
|
e338e70972 | ||
|
|
4c01782d6c | ||
|
|
5bf12fdd93 | ||
|
|
d209a10514 | ||
|
|
1d0eb68f70 | ||
|
|
b64d0c0d3c | ||
|
|
305ce1ae65 | ||
|
|
aed79ddbf7 | ||
|
|
3b5fc5e9cc | ||
|
|
e0bbbbcd45 | ||
|
|
5f23b3c272 | ||
|
|
bf7729b936 | ||
|
|
7c53558454 | ||
|
|
f2443f5e21 | ||
|
|
2b5386ad98 | ||
|
|
c2b407189e | ||
|
|
7de11753a9 | ||
|
|
2d6a474c2c | ||
|
|
cc1d3a7e9e | ||
|
|
fe17e3fcc0 | ||
|
|
ef904a4544 | ||
|
|
207e4e27b3 | ||
|
|
bda5c85acf | ||
|
|
c9acd3c812 | ||
|
|
f20acbf9b9 | ||
|
|
ad09b498a3 | ||
|
|
7618aafb90 | ||
|
|
7a3bf533b3 | ||
|
|
723ea5e2e0 | ||
|
|
7463592988 | ||
|
|
198acc0648 | ||
|
|
f53e991d02 | ||
|
|
22f6f9dd72 | ||
|
|
019e0083b0 | ||
|
|
1931da83a5 | ||
|
|
5dff0ff37b | ||
|
|
1003821dad | ||
|
|
5399b7c299 | ||
|
|
969053fa87 | ||
|
|
eb646ef5d8 | ||
|
|
f14942edb0 | ||
|
|
18a07abeb4 | ||
|
|
e766ad774d | ||
|
|
b38cc74764 | ||
|
|
3df40dd8a9 | ||
|
|
9d95638cf8 | ||
|
|
d85624c58e | ||
|
|
5ca6377015 | ||
|
|
33834fdf19 | ||
|
|
8e60c968a1 | ||
|
|
41298c490e | ||
|
|
ee09a9e863 | ||
|
|
8bd29c1ab2 | ||
|
|
f1d0350356 | ||
|
|
2fa7ecdee5 | ||
|
|
8a5cba914b | ||
|
|
5fbf333c61 | ||
|
|
33c9485031 | ||
|
|
2e82b311ee | ||
|
|
8c07537bec | ||
|
|
c701e1877e | ||
|
|
7186878f84 | ||
|
|
aeef1417e1 | ||
|
|
76d341d6ca | ||
|
|
d1c00c6080 | ||
|
|
a48864bf20 | ||
|
|
bebb596846 | ||
|
|
af5899fb1a | ||
|
|
489a26c14c | ||
|
|
134d23bb07 | ||
|
|
8b5469ef5c | ||
|
|
e3414ef11e | ||
|
|
68ad464b67 | ||
|
|
db2759c497 | ||
|
|
8760d7037a | ||
|
|
d2f7ac4898 | ||
|
|
5fd2c71559 | ||
|
|
6414d0ddb8 | ||
|
|
ba97bdf2fa | ||
|
|
6c82628402 | ||
|
|
16b65973b7 | ||
|
|
db45698e25 | ||
|
|
6234dd5681 | ||
|
|
00d50593c1 | ||
|
|
881ca7c2d4 | ||
|
|
77eb6cfd71 | ||
|
|
21065b23c0 | ||
|
|
7693d0e624 | ||
|
|
2503537e36 | ||
|
|
d27d9a22bf | ||
|
|
6e1ecadcc4 | ||
|
|
690fbc8d3f | ||
|
|
3ba07ec0a8 | ||
|
|
7518306975 | ||
|
|
249361bb7f | ||
|
|
f633037d6b | ||
|
|
5dd36c95d6 | ||
|
|
6b3f814cd6 | ||
|
|
3a17a84434 | ||
|
|
916a5cb7dd | ||
|
|
d5484e15ca | ||
|
|
3d93f2cf56 | ||
|
|
8929e85fb1 | ||
|
|
f6c1407259 | ||
|
|
b79913240d | ||
|
|
4c24384243 | ||
|
|
1635ea90ca | ||
|
|
9724953b2f | ||
|
|
1266fd052e | ||
|
|
6c970efb09 | ||
|
|
a32ee3935d | ||
|
|
cc0c6bda42 | ||
|
|
1bee69ef15 | ||
|
|
3b0dd129ba | ||
|
|
c3c6215a24 | ||
|
|
8eca5ea4c6 | ||
|
|
e4c74ebe91 | ||
|
|
141aba9526 | ||
|
|
f5bb381191 | ||
|
|
b9151c6908 | ||
|
|
664b5aae21 | ||
|
|
b44b7365b5 | ||
|
|
23afc1b354 | ||
|
|
f711876347 | ||
|
|
5879d15692 | ||
|
|
30d4ea996b | ||
|
|
d2e5005fa0 | ||
|
|
865e381c7c | ||
|
|
8c1f1c4c52 | ||
|
|
c2414c498f | ||
|
|
4f8588b4bf | ||
|
|
98cb22c670 | ||
|
|
4a0aa6e608 | ||
|
|
88151b4287 | ||
|
|
3a42147d3e | ||
|
|
4631b57531 | ||
|
|
5417e59a50 | ||
|
|
9ac570cba7 | ||
|
|
3d1cd9fae4 | ||
|
|
3c72828500 | ||
|
|
d3ab764c76 | ||
|
|
eddb10d52c | ||
|
|
208493b6fd | ||
|
|
d46bc636de | ||
|
|
4dedb4d10a | ||
|
|
93b8856a20 | ||
|
|
07e1c91f1c | ||
|
|
2d8664d841 | ||
|
|
72227b8d83 | ||
|
|
34e2faa692 | ||
|
|
6347c32f7b | ||
|
|
a7f686af73 | ||
|
|
020fbdaf4f | ||
|
|
369e98c7ef | ||
|
|
29ad238307 | ||
|
|
ae03fac468 | ||
|
|
9534f9ee3a | ||
|
|
a25da734c1 | ||
|
|
0ac460563f | ||
|
|
8dd885a6c7 | ||
|
|
4926606841 | ||
|
|
d4d8a4e6c9 | ||
|
|
61a2586aed | ||
|
|
3be13ac10b | ||
|
|
8dabbd26e5 | ||
|
|
701f20cf04 | ||
|
|
3f23643121 | ||
|
|
37aafc5148 | ||
|
|
afc3752b33 | ||
|
|
cf1a4b83bf | ||
|
|
ba07c1d562 | ||
|
|
f5e3deafa9 | ||
|
|
43b393fb3f | ||
|
|
b5f96c3564 | ||
|
|
00d30f3dfd | ||
|
|
97a05cf95d | ||
|
|
1c829ae78a | ||
|
|
480d193b19 | ||
|
|
d96c8f2ce5 | ||
|
|
57c361b878 | ||
|
|
d62d5a8731 | ||
|
|
4fd62ae0af | ||
|
|
3be96d903b | ||
|
|
ae722375d8 | ||
|
|
5eb3695967 | ||
|
|
5847801a32 | ||
|
|
9b168423b8 | ||
|
|
6904678382 | ||
|
|
0f09d5e000 | ||
|
|
1eca2bc0ad | ||
|
|
f619cef67a | ||
|
|
0f034312a2 | ||
|
|
090d106376 | ||
|
|
cdf52507c0 | ||
|
|
8acaed750a | ||
|
|
79569f5a6f | ||
|
|
ed28eab835 | ||
|
|
37572113c3 | ||
|
|
1e56874338 | ||
|
|
d2639570bb | ||
|
|
f4b87b7ae7 | ||
|
|
755754e173 | ||
|
|
62bf5fa11d | ||
|
|
cd85c647ff | ||
|
|
64a0312bf7 | ||
|
|
59dbd75ca2 | ||
|
|
ddff1f4c0d | ||
|
|
375db97f1c | ||
|
|
8f3600c49a | ||
|
|
7f9b5c0515 | ||
|
|
b3130edd98 | ||
|
|
873f4bacd6 | ||
|
|
900b9174d3 | ||
|
|
dd4ea20656 | ||
|
|
0b403c3313 | ||
|
|
c7e9c698a5 | ||
|
|
8e80347022 | ||
|
|
977d1f474b | ||
|
|
646682b056 | ||
|
|
62ce30fb38 | ||
|
|
604ec4a50d | ||
|
|
9834e91c6d | ||
|
|
3254bc2bef | ||
|
|
5caf045ca0 | ||
|
|
71cac12479 | ||
|
|
d015f4c25a | ||
|
|
f7a35e8da3 | ||
|
|
519c83ea5a | ||
|
|
3cd4364566 | ||
|
|
495d19982d | ||
|
|
6b6ed48095 | ||
|
|
a0805e8411 | ||
|
|
9df0594573 | ||
|
|
0926df9c54 | ||
|
|
b001580334 | ||
|
|
25804f1725 | ||
|
|
04fcc393fd | ||
|
|
46173f6486 | ||
|
|
6430e43f0e | ||
|
|
dbc7018a11 | ||
|
|
9cb5c75b4e | ||
|
|
1ad47fc7b1 | ||
|
|
2a558885ad | ||
|
|
0f8c3b8d07 | ||
|
|
87e4dd8a0b | ||
|
|
97f3333194 | ||
|
|
f1ea4793bb | ||
|
|
07ff3088bf | ||
|
|
6a22a11187 | ||
|
|
b05fe2355e | ||
|
|
480296c185 | ||
|
|
d026dd6821 | ||
|
|
417174388b | ||
|
|
2ca482f060 | ||
|
|
750ae663fc | ||
|
|
0abbba0309 | ||
|
|
6130a10d14 | ||
|
|
52827f4a46 | ||
|
|
e96e0e3dbb | ||
|
|
876d466563 | ||
|
|
83b902c86b | ||
|
|
ee4bd71855 | ||
|
|
dcfd85e933 | ||
|
|
a4e003ebf1 | ||
|
|
2732a183f3 | ||
|
|
0d217c680a | ||
|
|
eb0da565f3 | ||
|
|
903017285b | ||
|
|
f1fa380bdd | ||
|
|
699bdf94a0 | ||
|
|
cc71e1f567 | ||
|
|
6708f6a5f4 | ||
|
|
923c00fd5f | ||
|
|
256043a3a1 | ||
|
|
4aacf2fcea | ||
|
|
c8c8844fff | ||
|
|
07021da671 | ||
|
|
f271920936 | ||
|
|
f0482dba20 | ||
|
|
8e553f5e4c | ||
|
|
df280f956b | ||
|
|
fb5bed1416 |
3
.github/ISSUE_TEMPLATE.md
vendored
Normal file
3
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<!--
|
||||
Thanks for raising a Spring Session issue. Please provide a brief description of your problem along with the version of Spring Session that you are using. If possible, please also consider putting together a sample application that reproduces the issue.
|
||||
-->
|
||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<!--
|
||||
Thanks for contributing to Spring Session. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
|
||||
-->
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -7,4 +7,8 @@ bin
|
||||
.settings
|
||||
.project
|
||||
target
|
||||
out
|
||||
out
|
||||
.springBeans
|
||||
*.rdb
|
||||
!eclispe/.checkstyle
|
||||
.checkstyle
|
||||
19
.travis.yml
Normal file
19
.travis.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
language: java
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
script: ./gradlew build
|
||||
44
CODE_OF_CONDUCT.adoc
Normal file
44
CODE_OF_CONDUCT.adoc
Normal file
@@ -0,0 +1,44 @@
|
||||
= Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open
|
||||
and welcoming community, we pledge to respect all people who contribute through reporting
|
||||
issues, posting feature requests, updating documentation, submitting pull requests or
|
||||
patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this
|
||||
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
|
||||
that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
|
||||
consistently applying these principles to every aspect of managing this project. Project
|
||||
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
|
||||
from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an
|
||||
individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
|
||||
be reviewed and investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
|
||||
with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the
|
||||
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
||||
26
CONTRIBUTING.adoc
Normal file
26
CONTRIBUTING.adoc
Normal file
@@ -0,0 +1,26 @@
|
||||
= Contributing to Spring Session
|
||||
|
||||
Spring Session is released under the Apache 2.0 license. If you would like to contribute
|
||||
something, or simply want to hack on the code this document should help you get started.
|
||||
|
||||
== Code of Conduct
|
||||
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
== Using GitHub issues
|
||||
|
||||
We use GitHub issues to track bugs and enhancements. If you have a general usage question
|
||||
please ask on https://stackoverflow.com[Stack Overflow]. The Spring Session team and the
|
||||
broader community monitor the https://stackoverflow.com/tags/spring-session[`spring-session`]
|
||||
tag.
|
||||
|
||||
If you are reporting a bug, please help to speed up problem diagnosis by providing as much
|
||||
information as possible. Ideally, that would include a small sample project that
|
||||
reproduces the problem.
|
||||
|
||||
|
||||
|
||||
== Sign the Contributor License Agreement
|
||||
If you have not previously done so, please fill out and
|
||||
submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
||||
|
||||
189
README.adoc
189
README.adoc
@@ -1,17 +1,11 @@
|
||||
image:https://badges.gitter.im/spring-projects/spring-session.svg[link="https://gitter.im/spring-projects/spring-session?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
|
||||
image:https://travis-ci.org/spring-projects/spring-session.svg?branch=master["Build Status", link="https://travis-ci.org/spring-projects/spring-session"]
|
||||
|
||||
= Spring Session
|
||||
Rob Winch
|
||||
1.0.0.BUILD-SNAPSHOT
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:sectanchors:
|
||||
:icons: font
|
||||
:source-highlighter: prettify
|
||||
:idseparator: -
|
||||
:idprefix:
|
||||
:doctype: book
|
||||
:spring-session-version: 1.0.0.BUILD-SNAPSHOT
|
||||
|
||||
Spring Session aims to provide a common infrastructure for managing sessions. This allows for:
|
||||
Spring Session aims to provide a common infrastructure for managing sessions. This provides many benefits including:
|
||||
|
||||
* Accessing a session from any environment (i.e. web, messaging infrastructure, etc)
|
||||
* In a web environment
|
||||
@@ -19,173 +13,14 @@ Spring Session aims to provide a common infrastructure for managing sessions. Th
|
||||
** Pluggable strategy for determining the session id
|
||||
** Easily keep the HttpSession alive when a WebSocket is active
|
||||
|
||||
= Quick Start
|
||||
== Code of Conduct
|
||||
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
This section describes how to use Spring Session to use Redis when interacting with a web application's HttpSession. If you'd like to skip the reading, you can also refer to the <<sample>>
|
||||
= Spring Session Project Site
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use the project, you must ensure to update your dependencies. Instructions for building with Maven and Gradle have been provided below:
|
||||
You can find the documentation, issue management, support, samples, and guides for using Spring Session at https://projects.spring.io/spring-session/
|
||||
|
||||
* <<building-with-maven>>
|
||||
* <<building-with-gradle>>
|
||||
= License
|
||||
|
||||
=== Building with Maven
|
||||
|
||||
The project is available in the https://github.com/spring-projects/spring-framework/wiki/SpringSource-repository-FAQ[Spring Maven Repository]. If you are using Maven, you will want to make the following updates.
|
||||
|
||||
**Using the latest Snapshot in Maven**
|
||||
|
||||
If you want the latest snapshot, ensure you have the following repository in your pom.xml:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
----
|
||||
|
||||
Then ensure you have added the dependency:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-redis</artifactId>
|
||||
<version>1.3.0.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>2.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
=== Building with Gradle
|
||||
|
||||
**Using the latest Snapshot in Gradle**
|
||||
|
||||
If you want the latest snapshot, ensure you have the following repository in your pom.xml:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/libs-snapshot' }
|
||||
}
|
||||
----
|
||||
|
||||
Then ensure you have added the dependency:
|
||||
|
||||
[source,groovy]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.springframework.session:spring-session:{spring-session-version}",
|
||||
"org.springframework:spring-web:{spring-version}",
|
||||
"org.springframework.data:spring-data-redis:1.3.0.RELEASE",
|
||||
"redis.clients:jedis:2.4.1",
|
||||
"org.apache.commons:commons-pool2:2.2"
|
||||
}
|
||||
----
|
||||
|
||||
== Spring Configuration
|
||||
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public JedisConnectionFactory connectionFactory() throws Exception {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String,Session> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Session> template = new RedisTemplate<String, Session>();
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisOperationsSessionRepository sessionRepository(RedisTemplate<String, Session> redisTemplate) {
|
||||
return new RedisOperationsSessionRepository(redisTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SessionRepositoryFilter sessionFilter(RedisOperationsSessionRepository sessionRepository) {
|
||||
return new SessionRepositoryFilter(sessionRepository);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In our example, we are connecting to the default port (6379). For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
|
||||
== Servlet Initialization
|
||||
|
||||
We next need to be sure our Servlet Container (i.e. Tomcat) is properly configured.
|
||||
|
||||
. First we need ensure that our `Config` class from above was loaded. In the example below we do this by extending `AbstractContextLoaderInitializer` and implementing `createRootApplicationContext`.
|
||||
. Next we need to be sure the `SessionRepositoryFilter` is regsitered with the Servlet Container. We can do this by mapping a `DelegatingFilterProxy` to every request with the same name as the bean name of our `SessionRepositoryFilter`. In our instance, the bean name is the method name we used to create our `SessionRepositoryFilter`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class Initializer extends AbstractContextLoaderInitializer {
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
super.onStartup(servletContext);
|
||||
servletContext.addFilter("sessionFilter", DelegatingFilterProxy.class)
|
||||
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext createRootApplicationContext() {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(Config.class);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
= Sample
|
||||
|
||||
The code contains a https://github.com/spring-projects/spring-session/tree/master/samples/web[sample web application]. To run the sample:
|
||||
|
||||
. Obtain the source by https://github.com/spring-projects/spring-session[cloning the repository] or https://github.com/spring-projects/spring-session/archive/master.zip[downloading] it.
|
||||
. Run the application using gradle
|
||||
.. Linux / OSX `./gradlew tomcatRun`
|
||||
.. Windows `.\gradlew.bat tomcatRun`
|
||||
. Visit http://localhost:8080/
|
||||
|
||||
= Benefits
|
||||
|
||||
* This can make clustering much easier. This is nice because the clustering setup is done in a vendor neutral way. Furthermore, in some environments (i.e. PaaS solutions) developers cannot modify the cluster settings easily.
|
||||
* We can use different strategies for determining the session id. This gives us at least a few benefits
|
||||
** Allowing for a single browser to have multiple simultaneous sessions in a transparent fashion. For example, many developers wish to allow a user to authenticate with multiple accounts and switch between them similar to how you can in gmail.
|
||||
** When using a REST API, the session can be specified using a header instead of the JSESSIONID cookie (which leaks implementation details to the client). Many would argue that session is bad in REST because it has state, but it is important to note that session is just a form of cache and used responsibly it will increase performance & security.
|
||||
** When a session id is acquired in a header, we can default CSRF protection to off. This is because if the session id is found in the header we know that it is impossible to be a CSRF attack since, unlike cookies, headers must be manually populated.
|
||||
* We can easily keep the HttpSession and WebSocket Session in sync. Imagine a web application like gmail where you can authenticate and either write emails (HTTP requests) or chat (WebSocket). In standard servlet environment there is no way to keep the HttpSession alive through the WebSocket so you must ping the server. With our own session strategy we can have the WebSocket messages automatically keep the HttpSession alive. We can also destroy both sessions at once easily.
|
||||
* We can provide hooks to allow users to invalidate sessions that should not be active. For example, if you look in the lower right of gmail you can see the last account activity and click "Details". This shows a listing of all the active sessions along with the IP address, location, and browser information for your account.
|
||||
** Users can look through this and determine if anything is suspicious (i.e. if their account has a session that is associated to a country they have never been) and invalidate that session and change their password.
|
||||
** Another useful example is perhaps they checked their mail at the library and forgot to log out. With this custom mechanism this is very possible.
|
||||
* Spring Security currently supports restricting the number of concurrent sessions each user can have. The implementation works, but does so passively since we cannot get a handle to the session from the session id. Specifically, each time a user requests a page we check to see if that session id is valid in a separate data store. If it is no longer valid, we invalidate the session. With this new mechanism we can invalidate the session from the session id.
|
||||
Spring Session is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
|
||||
85
build.gradle
85
build.gradle
@@ -1,35 +1,72 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.6")
|
||||
classpath("org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE")
|
||||
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||
classpath('org.asciidoctor:asciidoctor-gradle-plugin:0.7.0')
|
||||
classpath('org.asciidoctor:asciidoctor-java-integration:0.1.4.preview.1')
|
||||
}
|
||||
repositories {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
|
||||
classpath("io.spring.gradle:spring-io-plugin:0.0.4.RELEASE")
|
||||
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.sonarqube" version "1.2"
|
||||
}
|
||||
|
||||
group = 'org.springframework.session'
|
||||
|
||||
ext.springBootVersion = '1.3.2.RELEASE'
|
||||
ext.IDE_GRADLE = "$rootDir/gradle/ide.gradle"
|
||||
ext.JAVA_GRADLE = "$rootDir/gradle/java.gradle"
|
||||
ext.SPRING3_GRADLE = "$rootDir/gradle/spring3.gradle"
|
||||
ext.MAVEN_GRADLE = "$rootDir/gradle/publish-maven.gradle"
|
||||
ext.SAMPLE_GRADLE = "$rootDir/gradle/sample.gradle"
|
||||
ext.TOMCAT_GRADLE = "$rootDir/gradle/tomcat.gradle"
|
||||
ext.TOMCAT_6_GRADLE = "$rootDir/gradle/tomcat6.gradle"
|
||||
ext.TOMCAT_7_GRADLE = "$rootDir/gradle/tomcat7.gradle"
|
||||
|
||||
apply plugin: 'sonar-runner'
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
apply plugin: 'base'
|
||||
|
||||
|
||||
sonarRunner {
|
||||
sonarProperties {
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
property "sonar.projectName", "Spring Session"
|
||||
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
|
||||
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
|
||||
property "sonar.links.ci", 'https://build.spring.io/browse/SESSION'
|
||||
property "sonar.links.issue", 'https://github.com/spring-projects/spring-session/issues'
|
||||
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
|
||||
property "sonar.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
}
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
property "sonar.projectName", "Spring Session"
|
||||
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
|
||||
property "sonar.links.homepage", 'https://github.com/spring-projects/spring-session'
|
||||
property "sonar.links.ci", 'https://build.spring.io/browse/SESSION'
|
||||
property "sonar.links.issue", 'https://github.com/spring-projects/spring-session/issues'
|
||||
property "sonar.links.scm", 'https://github.com/spring-projects/spring-session'
|
||||
property "sonar.links.scm_dev", 'https://github.com/spring-projects/spring-session.git'
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
}
|
||||
}
|
||||
|
||||
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
|
||||
project.tasks.docsZip.from(project(':docs').asciidoctor) {
|
||||
into('reference')
|
||||
}
|
||||
project.tasks.docsZip.from(project(':spring-session').javadoc) {
|
||||
into('api')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task docsZip(type: Zip, dependsOn: 'configDocsZip') {
|
||||
group = "Distribution"
|
||||
baseName = "spring-session"
|
||||
classifier = "docs"
|
||||
description = "Builds -${classifier} archive containing api and reference " +
|
||||
"for deployment."
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives docsZip
|
||||
}
|
||||
166
config/checkstyle/checkstyle.xml
Normal file
166
config/checkstyle/checkstyle.xml
Normal file
@@ -0,0 +1,166 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"https://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<!-- Suppressions -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${configDir}/suppressions.xml"/>
|
||||
</module>
|
||||
|
||||
<!-- Root Checks -->
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="${configDir}/header.txt"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
<property name="fileExtensions" value="java,xml"/>
|
||||
</module>
|
||||
|
||||
<!-- TreeWalker Checks -->
|
||||
<module name="TreeWalker">
|
||||
<!-- Annotations -->
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="elementStyle" value="compact"/>
|
||||
</module>
|
||||
<module name="MissingOverride"/>
|
||||
<module name="PackageAnnotation"/>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="allowSamelineSingleParameterlessAnnotation" value="false" />
|
||||
</module>
|
||||
|
||||
<!-- Block Checks -->
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text"/>
|
||||
</module>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
|
||||
<!-- Class Design -->
|
||||
<module name="FinalClass"/>
|
||||
<module name="InterfaceIsType"/>
|
||||
<module name="HideUtilityClassConstructor"/>
|
||||
<module name="MutableException"/>
|
||||
<module name="InnerTypeLast"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
|
||||
<!-- Coding -->
|
||||
<module name="CovariantEquals"/>
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="InnerAssignment"/>
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
<module name="StringLiteralEquality"/>
|
||||
<module name="NestedForDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="NestedIfDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="NestedTryDepth">
|
||||
<property name="max" value="3"/>
|
||||
</module>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="RequireThis">
|
||||
<property name="checkMethods" value="false"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
|
||||
<!-- Imports -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="AvoidStaticImport">
|
||||
<property name="excludes"
|
||||
value="org.assertj.core.api.Assertions.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.AdditionalMatchers.*, org.mockito.Matchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultHandlers.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo"/>
|
||||
</module>
|
||||
<module name="IllegalImport"/>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="UnusedImports">
|
||||
<property name="processJavadoc" value="true"/>
|
||||
</module>
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="java,/^javax?\./,*,org.springframework"/>
|
||||
<property name="ordered" value="true"/>
|
||||
<property name="separated" value="true"/>
|
||||
<property name="option" value="bottom"/>
|
||||
<property name="sortStaticImportsAlphabetically" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Javadoc Comments -->
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="package"/>
|
||||
<property name="authorFormat" value=".+\s.+"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
</module>
|
||||
<module name="JavadocVariable">
|
||||
<property name="scope" value="public"/>
|
||||
</module>
|
||||
<module name="JavadocStyle">
|
||||
<property name="checkEmptyJavadoc" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="JavadocTagContinuationIndentation">
|
||||
<property name="offset" value="0"/>
|
||||
</module>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
|
||||
<property name="tagOrder" value="@param, @author, @since, @see, @version, @serial, @deprecated"/>
|
||||
</module>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="target" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
<property name="tagOrder" value="@param, @return, @throws, @since, @deprecated, @see"/>
|
||||
</module>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<module name="CommentsIndentation"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
|
||||
<!-- Modifiers -->
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!-- Regexp -->
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="^\t* +\t*\S"/>
|
||||
<property name="message" value="Line has leading space characters; indentation should be performed with tabs only."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.mockito\.Mockito\.(when|doThrow|doAnswer)"/>
|
||||
<property name="message"
|
||||
value="Please use BDDMockto imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.junit\.Assert\.assert"/>
|
||||
<property name="message" value="Please use AssertJ imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<module name="Regexp">
|
||||
<property name="format" value="[ \t]+$"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
<property name="message" value="Trailing whitespace"/>
|
||||
</module>
|
||||
|
||||
<!-- Whitespace -->
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceAfter">
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<module name="ParenPad"/>
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
</module>
|
||||
</module>
|
||||
16
config/checkstyle/header.txt
Normal file
16
config/checkstyle/header.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
^\Q/*\E$
|
||||
^\Q * Copyright 2014-\E20\d\d\Q the original author or authors.\E$
|
||||
^\Q *\E$
|
||||
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
|
||||
^\Q * you may not use this file except in compliance with the License.\E$
|
||||
^\Q * You may obtain a copy of the License at\E$
|
||||
^\Q *\E$
|
||||
^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q *\E$
|
||||
^\Q * Unless required by applicable law or agreed to in writing, software\E$
|
||||
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
|
||||
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
|
||||
^\Q * See the License for the specific language governing permissions and\E$
|
||||
^\Q * limitations under the License.\E$
|
||||
^\Q */\E$
|
||||
^.*$
|
||||
18
config/checkstyle/suppressions.xml
Normal file
18
config/checkstyle/suppressions.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor"/>
|
||||
|
||||
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="Javadoc"/>
|
||||
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc"/>
|
||||
|
||||
<suppress files="[\\/]docs[\\/]" checks="Javadoc"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="CommentsIndentation"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
|
||||
|
||||
<suppress files="[\\/]samples[\\/]" checks="Javadoc"/>
|
||||
<suppress files="[\\/]samples[\\/]" checks="CommentsIndentation"/>
|
||||
<suppress files="[\\/]samples[\\/]" checks="InnerTypeLast"/>
|
||||
</suppressions>
|
||||
75
docs/build.gradle
Normal file
75
docs/build.gradle
Normal file
@@ -0,0 +1,75 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'org.kordamp.gradle:livereload-gradle-plugin:0.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'org.kordamp.gradle.livereload'
|
||||
apply from: JAVA_GRADLE
|
||||
apply plugin: 'org.asciidoctor.convert'
|
||||
|
||||
liveReload {
|
||||
docRoot asciidoctor.sourceDir.canonicalPath
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'http://dist.gemstone.com/maven/release' }
|
||||
}
|
||||
|
||||
asciidoctorj {
|
||||
|
||||
}
|
||||
|
||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||
|
||||
dependencies {
|
||||
testCompile project(':spring-session'),
|
||||
project(':spring-session-data-mongo'),
|
||||
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
|
||||
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
|
||||
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
|
||||
"org.springframework:spring-websocket:${springVersion}",
|
||||
"org.springframework:spring-messaging:${springVersion}",
|
||||
"org.springframework:spring-jdbc:${springVersion}",
|
||||
"org.springframework.security:spring-security-web:${springSecurityVersion}",
|
||||
"org.springframework.security:spring-security-test:${springSecurityVersion}",
|
||||
'junit:junit:4.11',
|
||||
'org.mockito:mockito-core:1.9.5',
|
||||
"org.springframework:spring-test:$springVersion",
|
||||
"org.assertj:assertj-core:$assertjVersion",
|
||||
"com.hazelcast:hazelcast:$hazelcastVersion",
|
||||
"redis.clients:jedis:2.4.1",
|
||||
"javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
}
|
||||
|
||||
asciidoctor {
|
||||
def ghTag = snapshotBuild ? 'master' : project.version
|
||||
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag/"
|
||||
attributes 'version-snapshot': snapshotBuild,
|
||||
'version-milestone': milestoneBuild,
|
||||
'version-release': releaseBuild,
|
||||
'gh-url': ghUrl,
|
||||
'gh-samples-url': "$ghUrl/samples/",
|
||||
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
|
||||
'spring-session-version' : version,
|
||||
'spring-version' : springVersion,
|
||||
'hazelcast-version' : hazelcastVersion,
|
||||
'docs-itest-dir' : rootProject.projectDir.path + '/docs/src/integration-test/java/',
|
||||
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
|
||||
'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/',
|
||||
'samples-dir' : rootProject.projectDir.path + '/samples/',
|
||||
'session-main-resources-dir' : rootProject.projectDir.path + '/spring-session/src/main/resources/',
|
||||
|
||||
'source-highlighter' : 'coderay',
|
||||
'imagesdir':'./images',
|
||||
'icons': 'font',
|
||||
'sectanchors':'',
|
||||
'idprefix':'',
|
||||
'idseparator':'-',
|
||||
'docinfo1':'true',
|
||||
'revnumber' : project.version
|
||||
}
|
||||
162
docs/src/docs/asciidoc/guides/boot.adoc
Normal file
162
docs/src/docs/asciidoc/guides/boot.adoc
Normal file
@@ -0,0 +1,162 @@
|
||||
= Spring Session - Spring Boot
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Spring Boot.
|
||||
|
||||
NOTE: The completed guide can be found in the <<boot-sample, boot sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
We assume you are working with a working Spring Boot web application.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[boot-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}boot/src/main/java/sample/config/HttpSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
|
||||
[[boot-redis-configuration]]
|
||||
== Configuring the Redis Connection
|
||||
|
||||
Spring Boot automatically creates a `RedisConnectionFactory` that connects Spring Session to a Redis Server on localhost on port 6379 (default port).
|
||||
In a production environment you need to ensure to update your configuration to point to your Redis server.
|
||||
For example, you can include the following in your *application.properties*
|
||||
|
||||
.src/main/resources/application.properties
|
||||
----
|
||||
spring.redis.host=localhost
|
||||
spring.redis.password=secret
|
||||
spring.redis.port=6379
|
||||
----
|
||||
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
|
||||
|
||||
[[boot-servlet-configuration]]
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<boot-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Boot takes care of both of these steps for us.
|
||||
|
||||
[[boot-sample]]
|
||||
== boot Sample Application
|
||||
|
||||
The boot Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Spring Boot.
|
||||
|
||||
[[boot-running]]
|
||||
=== Running the boot Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:boot:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
[[boot-explore]]
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Redis rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
[[boot-how]]
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by Redis.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Redis.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
101
docs/src/docs/asciidoc/guides/custom-cookie.adoc
Normal file
101
docs/src/docs/asciidoc/guides/custom-cookie.adoc
Normal file
@@ -0,0 +1,101 @@
|
||||
= Spring Session - Custom Cookie
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
|
||||
The guide assumes you have already link:./httpsession.html[setup Spring Session in your project].
|
||||
|
||||
NOTE: The completed guide can be found in the <<custom-cookie-sample, Custom Cookie sample application>>.
|
||||
|
||||
[[custom-cookie-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
Once you have setup Spring Session you can easily customize how the session cookie is written by exposing a `CookieSerializer` as a Spring Bean.
|
||||
Out of the box, Spring Session comes with `DefaultCookieSerializer`.
|
||||
Simply exposing the `DefaultCookieSerializer` as a Spring Bean will augment the existing configuration when using configurations like `@EnableRedisHttpSession`.
|
||||
You can find an example of customizing Spring Session's cookie below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}custom-cookie/src/main/java/sample/Config.java[tags=cookie-serializer]
|
||||
----
|
||||
|
||||
<1> We customize the name of the cookie to be JSESSIONID
|
||||
<2> We customize the path of the cookie to be "/" (rather than the default of the context root)
|
||||
<3> We customize the domain name pattern (a regular expression) to be `^.+?\\.(\\w+\\.[a-z]+)$`
|
||||
This allows sharing a session across domains and applications.
|
||||
If the regular expression does not match, no domain is set and the existing domain will be used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
|
||||
This means that a request to https://child.example.com will set the domain to example.com.
|
||||
However, a request to http://localhost:8080/ or https://192.168.1.100:8080/ will leave the cookie unset and thus still work in development without any changes necessary for production.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
|
||||
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
====
|
||||
|
||||
[[custom-cookie-options]]
|
||||
== Configuration Options
|
||||
|
||||
The configuration options available are:
|
||||
|
||||
* `cookieName` - the name of the cookie to use
|
||||
Default "SESSION"
|
||||
* `useSecureCookie` - specify if a secure cookie be used
|
||||
Default use value of `HttpServletRequest.isSecure()` at the time of creation.
|
||||
* `cookiePath` - the path of the cookie
|
||||
Default is context root
|
||||
* `cookieMaxAge` - specifies the max age of the cookie to be set at the time the session is created.
|
||||
Default is -1 which indicates the cookie will be removed when the browser is closed.
|
||||
* `jvmRoute` - specifies a suffix to be appended to the session id and included in the cookie.
|
||||
Used to identify which JVM to route to for session affinity.
|
||||
With some implementations (i.e. Redis) this provides no performance benefit.
|
||||
However, this can help with tracing logs of a particular user.
|
||||
* `domainName` - allows specifying a specific domain name to be used for the cookie.
|
||||
This option is simple to understand, but will likely require a different configuration between development and production environments.
|
||||
See domainNamePattern as an alternative.
|
||||
* `domainNamePattern` - a case insensitive pattern used to extract the domain name from the `HttpServletRequest#getServerName()`.
|
||||
The pattern should provide a single grouping used to extract the value of the cookie domain.
|
||||
If the regular expression does not match, no domain is set and the existing domain will be used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
|
||||
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
====
|
||||
|
||||
|
||||
[[custom-cookie-sample]]
|
||||
== custom-cookie Sample Application
|
||||
|
||||
|
||||
|
||||
=== Running the custom-cookie Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:custom-cookie:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the custom-cookie Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button.
|
||||
You should now see the values displayed in the table.
|
||||
|
||||
If you look at the cookies for the application, you can see the cookie is saved to the custom name of JSESSIONID
|
||||
141
docs/src/docs/asciidoc/guides/findbyusername.adoc
Normal file
141
docs/src/docs/asciidoc/guides/findbyusername.adoc
Normal file
@@ -0,0 +1,141 @@
|
||||
= Spring Session - find by username
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to find sessions by username.
|
||||
|
||||
NOTE: The completed guide can be found in the <<findbyusername-sample, findbyusername application>>.
|
||||
|
||||
NOTE: This feature will likely be refactored in the next release to account for https://github.com/spring-projects/spring-session/issues/301[#301]
|
||||
|
||||
|
||||
[[findbyusername-assumptions]]
|
||||
== Assumptions
|
||||
|
||||
The guide assumes you have already added Spring Session using the built in Redis configuration support to your application.
|
||||
The guide also assumes you have already applied Spring Security to your application.
|
||||
However, we the guide will be somewhat general purpose and can be applied to any technology with minimal changes we will discuss.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you need to learn how to add Spring Session to your project, please refer to the listing of link:../#samples[samples and guides]
|
||||
====
|
||||
|
||||
== About the Sample
|
||||
|
||||
Our sample is using this feature to invalidate the users session that might have been compromised.
|
||||
Consider the following scenario:
|
||||
|
||||
* User goes to library and authenticates to the application
|
||||
* User goes home and realizes they forgot to log out
|
||||
* User can log in and terminate the session from the library using clues like the location, created time, last accessed time, etc.
|
||||
|
||||
Wouldn't it be nice if we could allow the user to invalidate the session at the library from any device they authenticate with?
|
||||
This sample demonstrates how this is possible.
|
||||
|
||||
[[findbyprincipalnamesessionrepository]]
|
||||
== FindByIndexNameSessionRepository
|
||||
|
||||
In order to look up a user by their username, you must first choose a `SessionRepository` that implements <<index.doc#api-findbyprincipalnamesessionrepository,FindByIndexNameSessionRepository>>.
|
||||
Our sample application assumes that the Redis support is already setup, so we are ready to go.
|
||||
|
||||
== Mapping the username
|
||||
|
||||
`FindByIndexNameSessionRepository` can only find a session by the username, if the developer instructs Spring Session what user is associated with the `Session`.
|
||||
This is done by ensuring that the session attribute with the name `FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
|
||||
|
||||
Generally, speaking this can be done with the following code immediately after the user authenticates:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=set-username]
|
||||
----
|
||||
|
||||
== Mapping the username with Spring Security
|
||||
|
||||
Since we are using Spring Security, the user name is automatically indexed for us.
|
||||
This means we will not have to perform any steps to ensure the user name is indexed.
|
||||
|
||||
== Adding Additional Data to Session
|
||||
|
||||
It may be nice to associate additional information (i.e. IP Address, the browser, location, etc) to the session.
|
||||
This makes it easier for the user to know which session they are looking at.
|
||||
|
||||
To do this simply determine which session attribute you want to use and what information you wish to provide.
|
||||
Then create a Java bean that is added as a session attribute.
|
||||
For example, our sample application includes the location and access type of the session
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}findbyusername/src/main/java/sample/session/SessionDetails.java[tags=class]
|
||||
----
|
||||
|
||||
We then inject that information into the session on each HTTP request using a `SessionDetailsFilter`.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}findbyusername/src/main/java/sample/session/SessionDetailsFilter.java[tags=dofilterinternal]
|
||||
----
|
||||
|
||||
We obtain the information we want and then set the `SessionDetails` as an attribute in the `Session`.
|
||||
When we retrieve the `Session` by username, we can then use the session to access our `SessionDetails` just like any other session attribute.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
You might be wondering at this point why Spring Session does not provide `SessionDetails` functionality out of the box.
|
||||
The reason, is twofold.
|
||||
The first is that it is very trivial for applications to implement this themselves.
|
||||
The second reason is that the information that is populated in the session (and how frequently that information is updated) is highly application dependent.
|
||||
====
|
||||
|
||||
== Finding sessions for a specific user
|
||||
|
||||
We can now find all the sessions for a specific user.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}findbyusername/src/main/java/sample/mvc/IndexController.java[tags=findbyusername]
|
||||
----
|
||||
|
||||
In our instance, we find all sessions for the currently logged in user.
|
||||
However, this could easily be modified for an administrator to use a form to specify which user to look up.
|
||||
|
||||
[[findbyusername-sample]]
|
||||
== findbyusername Sample Application
|
||||
|
||||
=== Running the findbyusername Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:findbyusername:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
You should also see a listing of active sessions for the currently logged in user.
|
||||
|
||||
Let's emulate the flow we discussed in the <<About the Sample>> section
|
||||
|
||||
* Open a new incognito window and navigate to http://localhost:8080/
|
||||
* Enter the following to log in:
|
||||
** **Username** _user_
|
||||
** **Password** _password_
|
||||
* Terminate your original session
|
||||
* Refresh the original window and see you are logged out
|
||||
129
docs/src/docs/asciidoc/guides/grails3.adoc
Normal file
129
docs/src/docs/asciidoc/guides/grails3.adoc
Normal file
@@ -0,0 +1,129 @@
|
||||
= Spring Session - Grails
|
||||
Eric Helgeson
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails 3.1
|
||||
|
||||
NOTE: Grails 3.1 is based off spring boot 1.3 so much of the advanced configuration and options can be found in the boot docs as well.
|
||||
|
||||
NOTE: The completed guide can be found in the <<grails3-sample, Grails 3 sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
We assume you are working with a working Grails 3.1 web profile.
|
||||
Add the following dependencies:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile 'org.springframework.boot:spring-boot-starter-redis'
|
||||
compile 'org.springframework.session:spring-session:{spring-session-version}'
|
||||
}
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
----
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.spring.io/libs-snapshot'
|
||||
}
|
||||
}
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
----
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.spring.io/libs-milestone'
|
||||
}
|
||||
}
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[grails3-redis-configuration]]
|
||||
== Configuring the Redis Connection
|
||||
|
||||
Spring Boot automatically creates a `RedisConnectionFactory` that connects Spring Session to a Redis Server on localhost on port 6379 (default port).
|
||||
In a production environment you need to ensure to update your configuration to point to your Redis server.
|
||||
For example, you can include the following in your *application.yml*
|
||||
|
||||
.grails-app/conf/application.yml
|
||||
[source,yml]
|
||||
----
|
||||
spring:
|
||||
redis:
|
||||
host: localhost
|
||||
password: secret
|
||||
port: 6397
|
||||
----
|
||||
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
|
||||
|
||||
[[grails3-sample]]
|
||||
== Grails 3 Sample Application
|
||||
|
||||
The Grails 3 Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails.
|
||||
|
||||
[[grails3-running]]
|
||||
=== Running the Grails 3 Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:grails3:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/test/index
|
||||
|
||||
[[grails3-explore]]
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Redis rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
[[grails3-how]]
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by Redis.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Redis.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/test/index and observe that we are no longer authenticated.
|
||||
191
docs/src/docs/asciidoc/guides/hazelcast-spring.adoc
Normal file
191
docs/src/docs/asciidoc/guides/hazelcast-spring.adoc
Normal file
@@ -0,0 +1,191 @@
|
||||
= Spring Session and Spring Security with Hazelcast
|
||||
Tommy Ludwig; Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session along with Spring Security using Hazelcast as your data store.
|
||||
It assumes you have already applied Spring Security to your application.
|
||||
|
||||
NOTE: The completed guide can be found in the <<hazelcast-spring-security-sample, Hazelcast Spring Security sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hazelcast</groupId>
|
||||
<artifactId>hazelcast</artifactId>
|
||||
<version>{hazelcast-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[security-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
|
||||
----
|
||||
|
||||
<1> The `@EnableHazelcastHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Hazelcast.
|
||||
<2> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
|
||||
By default, an embedded instance of Hazelcast is started and connected to by the application.
|
||||
For more information on configuring Hazelcast, refer to the https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<security-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our Config class to it.
|
||||
|
||||
.src/main/java/sample/SecurityInitializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}hazelcast-spring/src/main/java/sample/SecurityInitializer.java[tags=class]
|
||||
----
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
It is extremely important that Spring Session's `springSessionRepositoryFilter` is invoked before Spring Security's `springSecurityFilterChain`.
|
||||
This ensures that the `HttpSession` that Spring Security uses is backed by Spring Session.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}hazelcast-spring/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request before Spring Security's `springSecurityFilterChain`.
|
||||
|
||||
|
||||
|
||||
[[hazelcast-spring-security-sample]]
|
||||
== Hazelcast Spring Security Sample Application
|
||||
|
||||
=== Running the Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Hazelcast will run in embedded mode with your application by default, but if you want to connect
|
||||
to a stand alone instance instead, you can configure it by following the instructions in the
|
||||
https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:hazelcast-spring:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Hazelcast rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Hazelcast.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by a `Map` in Hazelcast.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Hazelcast.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
=== Interact with the data store
|
||||
|
||||
If you like, you can remove the session using https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-java-client[a Java client],
|
||||
https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#other-client-implementations[one of the other clients], or the
|
||||
https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#management-center[management center].
|
||||
|
||||
==== Using the console
|
||||
|
||||
For example, using the management center console after connecting to your Hazelcast node:
|
||||
|
||||
default> ns spring:session:sessions
|
||||
spring:session:sessions> m.clear
|
||||
|
||||
TIP: The Hazelcast documentation has instructions for https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#executing-console-commands[the console].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into the console ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
spring:session:sessions> m.remove 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
|
||||
==== Using the REST API
|
||||
|
||||
As described in the other clients section of the documentation, there is a
|
||||
https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#rest-client[REST API]
|
||||
provided by the Hazelcast node(s).
|
||||
|
||||
For example, you could delete an individual key as follows (replacing `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie):
|
||||
|
||||
$ curl -v -X DELETE http://localhost:xxxxx/hazelcast/rest/maps/spring:session:sessions/7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
TIP: The port number of the Hazelcast node will be printed to the console on startup. Replace `xxxxx` above with the port number.
|
||||
|
||||
Now observe that you are no longer authenticated with this session.
|
||||
263
docs/src/docs/asciidoc/guides/httpsession-gemfire-boot.adoc
Normal file
263
docs/src/docs/asciidoc/guides/httpsession-gemfire-boot.adoc
Normal file
@@ -0,0 +1,263 @@
|
||||
= Spring Session - HttpSession with GemFire Client/Server using Spring Boot
|
||||
John Blum
|
||||
:toc:
|
||||
|
||||
This guide describes how to build a _Spring Boot_ application configured with _Spring Session_ to transparently leverage
|
||||
Pivotal GemFire to back a web application's `HttpSession`.
|
||||
|
||||
In this sample, GemFire's client/server topology is employed using a pair of _Spring Boot_ applications, one to
|
||||
configure and run a GemFire Server and another to configure and run the client, Spring MVC-based web application
|
||||
making use of the `HttpSession`.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-gemfire-boot-sample,HttpSession with GemFire using Spring Boot Sample Application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before using _Spring Session_, you must ensure that the required dependencies are included.
|
||||
If you are using Maven, include the following `dependencies` in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-gemfire</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
[[httpsession-spring-java-configuration]]
|
||||
== Spring Boot Configuration
|
||||
|
||||
After adding the required dependencies and repository declarations, we can create our Spring configuration
|
||||
for both the GemFire client and server using _Spring Boot_. The Spring configuration is responsible for
|
||||
creating a Servlet Filter that replaces the `HttpSession` with an implementation backed by _Spring Session_
|
||||
and GemFire.
|
||||
|
||||
=== Spring Boot-based GemFire Server
|
||||
|
||||
We start with the _Spring Boot_ application for configuring and bootstrapping a GemFire Server process...
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-boot/src/main/java/sample/server/GemFireServer.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableGemFireHttpSession` annotation is used on the GemFire Server mainly to define the corresponding
|
||||
Region (e.g. `ClusteredSpringSessions`, the default) in which Session state information will be stored
|
||||
and managed by GemFire. As well, we have specified an arbitrary expiration attribute (i.e. `maxInactiveIntervalInSeconds`)
|
||||
for when the Session will timeout, which is triggered by a GemFire Region entry expiration event that also invalidates
|
||||
the Session object in the Region.
|
||||
<2> Next, we define a few `Properties` allowing us to configure certain aspects of the GemFire Server using
|
||||
https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
|
||||
<3> Then, we create an instance of the GemFire Cache using our defined `Properties`.
|
||||
<4> Finally, we setup a Cache Server instance running in the GemFire Server to listen for connections from cache clients.
|
||||
The Cache Server `Socket` will be used to connect our GemFire client cache, _Spring Boot_ web application to the server.
|
||||
|
||||
The sample also makes use of a `PropertySourcesPlaceholderConfigurer` bean in order to externalize the sample application
|
||||
configuration to affect GemFire and application configuration/behavior from the command-line (e.g. such as GemFire's
|
||||
`log-level` using the `gemfire.log.level` System property; more details below).
|
||||
|
||||
=== Spring Boot-based GemFire cache client Web application
|
||||
|
||||
Now, we create the _Spring Boot_ web application (a GemFire cache client), exposing our Web service
|
||||
using Spring MVC along with _Spring Session_ backed by GemFire, connected to our _Spring Boot_-based GemFire Server,
|
||||
in order to manage Session state in a cluster/replicated-enabled fashion.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-boot/src/main/java/sample/client/Application.java[tags=class]
|
||||
----
|
||||
|
||||
<1> Here, again, we use the `@EnableGemFireHttpSession` annotation to not only configure the GemFire cache client,
|
||||
but also to override the (HTTP) Web application container's `HttpSession` and replace it with a Session implementation
|
||||
backed by _Spring Session_ in conjunction with GemFire. Also notice, we did not define any Session expiration timeout
|
||||
with the `maxInactiveIntervalInSeconds` attribute this time. That is because the Session expiration is managed
|
||||
by GemFire, which will appropriately notify the cache client when the Session times out. Again, we have just resorted
|
||||
to using the default Region, `ClusteredSpringSessions`. Of course, we can change the Region name, but we must do so
|
||||
on both the client and the server. That is a GemFire requirement, not a _Spring Session Data GemFire_ requirement.
|
||||
<2> Similary to the server configuration, we set a few basic GemFire System `Properties` on the client.
|
||||
<3> Although, this time, an instance of `ClientCache` is created with the `ClientCacheFactoryBean` from _Spring Data GemFire_.
|
||||
<4> However, in order to connect to the GemFire Server we must define a GemFire `Pool` bean containing pre-populated
|
||||
connections to the server. Whenever a client Region entry operation corresponding to a Session update occurs,
|
||||
the client-side Region will use an existing, pooled connection to route the operation to the server.
|
||||
<5> The following _Spring_ `BeanPostProcessor` (along with some utility methods) are only needed for testing purposes
|
||||
and is not required by any production code. Specifically, the `BeanPostProcessor` along with the code referenced in *6*
|
||||
is useful in integration test cases where the client and server processes are forked by the test framework. It is pretty
|
||||
easy to figure out that a race condition is imminent without proper coordination between the client and the server,
|
||||
therefore, the BPP and `ClientMembershipListener` help sync the interaction between the client and the server
|
||||
on startup during automated testing.
|
||||
<6> See <5> above.
|
||||
<7> Navigates the Web application to the home page (`index.html`), which uses **Thymeleaf** templates for server-side
|
||||
pages.
|
||||
<8> Heartbeat web service endpoint (useful for manual testing purposes).
|
||||
<9> Web service endpoint for adding a Session attributes defined by the user using the Web application UI. In addition,
|
||||
the webapp stores an additional Session attribute (`requestCount`) to keep track of how many HTTP requests the user
|
||||
has sent during the current "session".
|
||||
|
||||
There are many other utility methods, so please refer to the actual source code for full details.
|
||||
|
||||
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds or thousands of GemFire data nodes
|
||||
(servers), it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator
|
||||
passes meta-data to clients about the servers available, their load and which servers have the client's data of interest,
|
||||
which is particularly important in direct, single-hop data access and latency-sensitive operations. See more details
|
||||
about the https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide].
|
||||
|
||||
NOTE: For more information on configuring _Spring Data GemFire_, refer to the https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
|
||||
|
||||
The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both _Spring Session_
|
||||
and GemFire out-of-the-box using the following attributes:
|
||||
|
||||
* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**).
|
||||
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*").
|
||||
* `clientRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
|
||||
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
|
||||
(default is `PROXY`). This attribute is only used when configuring client Region.
|
||||
* `serverRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
|
||||
using a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
|
||||
(default is `PARTITION`). This attribute is only used when configuring server Regions, or when a p2p topology is employed.
|
||||
|
||||
NOTE: It is important to note that the GemFire client Region name must match a server Region by the same name if
|
||||
the client Region is a `PROXY` or `CACHING_PROXY`. Client and server Region names are not required to match if
|
||||
the client Region used to store Spring Sessions is `LOCAL`. However, keep in mind that your session state will not
|
||||
be propagated to the server and you lose all the benefits of using GemFire to store and manage distributed, replicated
|
||||
session state information in a cluster.
|
||||
|
||||
[[httpsession-gemfire-boot-sample]]
|
||||
== HttpSession with GemFire using Spring Boot Sample Application
|
||||
|
||||
=== Running the httpsession-gemfire-boot Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following commands.
|
||||
|
||||
First, you must run the server:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-boot:run [-Dgemfire.log-level=config]
|
||||
----
|
||||
|
||||
Then, in a separate terminal, run the client:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-boot:bootRun [-Dgemfire.log-level=config]
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/. In this sample, the web application
|
||||
is the client cache and the server is standalone.
|
||||
|
||||
=== Exploring the httpsession-gemfire-boot Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _test_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the attribute name and value displayed in the table
|
||||
along with an additional attribute (`requestCount`) indicating the number of Session interactions (via HTTP requests).
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the the Spring MVC web service endpoint, shown here for convenience:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
@RequestMapping(method = RequestMethod.POST, path = "/session")
|
||||
public String session(HttpSession session, ModelMap modelMap,
|
||||
@RequestParam(name = "attributeName", required = false) String name,
|
||||
@RequestParam(name = "attributeValue", required = false) String value) {
|
||||
|
||||
modelMap.addAttribute("sessionAttributes",
|
||||
attributes(setAttribute(updateRequestCount(session), name, value)));
|
||||
|
||||
return INDEX_TEMPLATE_VIEW_NAME;
|
||||
}
|
||||
----
|
||||
|
||||
Instead of using the embedded HTTP server's `HttpSession`, we are actually persisting the Session state in GemFire.
|
||||
_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
|
||||
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
|
||||
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
|
||||
|
||||
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
|
||||
at the command-line:
|
||||
|
||||
$ gfsh
|
||||
|
||||
Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
|
||||
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
|
||||
|
||||
....
|
||||
gfsh>connect --jmx-manager=localhost[1099]
|
||||
|
||||
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
|
||||
|
||||
Result : true
|
||||
startCount : 0
|
||||
endCount : 20
|
||||
Rows : 1
|
||||
|
||||
Result
|
||||
------------------------------------
|
||||
70002719-3c54-4c20-82c3-e7faa6b718f3
|
||||
|
||||
NEXT_STEP_NAME : END
|
||||
|
||||
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
|
||||
....
|
||||
|
||||
NOTE: The _GemFire User Guide_ has more detailed instructions on using https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
|
||||
|
||||
Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed.
|
||||
|
||||
Alternatively, you can wait **20 seconds** for the session to expire and timeout, and then refresh the page. The attribute
|
||||
we added should no longer be displayed in the table.
|
||||
@@ -0,0 +1,272 @@
|
||||
= Spring Session - HttpSession with GemFire Client/Server using XML (Quick Start)
|
||||
John Blum
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Spring Session to transparently leverage Pivotal GemFire to back a web application's
|
||||
`HttpSession` using XML Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-gemfire-clientserver-xml-sample-app,HttpSession with GemFire (Client/Server) using XML Sample Application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before using Spring Session, you must ensure that the required dependencies are included.
|
||||
If you are using Maven, include the following `dependencies` in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-gemfire</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
[[httpsession-spring-xml-configuration]]
|
||||
== Spring XML Configuration
|
||||
|
||||
After adding the required dependencies and repository declarations, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession`
|
||||
with an implementation backed by Spring Session and GemFire.
|
||||
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/spring/session-client.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> First, a `Properties` bean is created to reference GemFire configuration common to both the client and server,
|
||||
stored in the `META-INF/spring/application.properties` file.
|
||||
<2> The `application.properties` are used along with the `PropertySourcesPlaceholderConfigurer` bean to replace
|
||||
placeholders in the Spring XML configuration meta-data with property values.
|
||||
<3> Spring annotation configuration support is enabled with `<context:annotation-config/>` element so that any
|
||||
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
|
||||
by Spring will be configured appropriately.
|
||||
<4> `GemFireHttpSessionConfiguration` is registered to enable Spring Session functionality.
|
||||
<5> Then, a Spring `BeanPostProcessor` is registered to determine whether a GemFire Server at the designated host/port
|
||||
is running, blocking client startup until the server is available.
|
||||
<6> Next, we include a `Properties` bean to configure certain aspects of the GemFire client cache using
|
||||
https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
|
||||
In this case, we are just setting GemFire's `log-level` from a sample application-specific System property, defaulting
|
||||
to `warning` if unspecified.
|
||||
<7> Finally, we create the GemFire client cache and configure a Pool of client connections to talk to the GemFire Server
|
||||
in our Client/Server topology. In our configuration, we use sensible settings for timeouts, number of connections
|
||||
and so on. Also, our `Pool` has been configured to connect directly to a server.
|
||||
|
||||
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers),
|
||||
it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data
|
||||
to clients about the servers available, load and which servers have the client's data of interest, which is particularly
|
||||
important for single-hop, direct data access. See more details about the https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide].
|
||||
|
||||
NOTE: For more information on configuring _Spring Data GemFire_, refer to the https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
|
||||
|
||||
=== Server Configuration
|
||||
|
||||
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
|
||||
session state information up to the server to manage.
|
||||
|
||||
In this sample, we will use the following GemFire Server Java Configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/resources/META-INF/spring/session-server.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> First, we enable Spring annotation config support with the `<context:annotation-config>` element so that any
|
||||
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
|
||||
by Spring will be configured appropriately.
|
||||
<2> A `PropertySourcesPlaceholderConfigurer` is registered to replace placeholders in our Spring XML configuration
|
||||
meta-data with property values from `META-INF/spring/application.properties`.
|
||||
<3> We enable the same Spring Session functionality that we used on the client by registering an instance of `GemFireHttpSessionConfiguration`,
|
||||
except that we set the session expiration timeout to **30 seconds**. We will explain later what this means.
|
||||
<4> Next, we configure the GemFire Server using GemFire System properties very much like our P2P samples.
|
||||
With the `mcast-port` set to 0 and no `locators` property specified, our server will be standalone. We also allow a
|
||||
JMX client (e.g. _Gfsh_) to connect to our server with the use of the GemFire-specific JMX System properties.
|
||||
<5> Then, we create an instance of the GemFire peer cache using our GemFire System properties.
|
||||
<6> And finally, we also setup a GemFire `CacheServer` instance running on *localhost*, listening on port **11235**,
|
||||
to accept our client connection.
|
||||
|
||||
The GemFire Server configuration gets bootstrapped with the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/java/sample/Application.java[tags=class]
|
||||
----
|
||||
|
||||
TIP: Instead of a simple Java class with a main method, you could also use _Spring Boot_.
|
||||
|
||||
<1> The `@Configuration` annotation designates this Java class as a source for Spring configuration meta-data using
|
||||
Spring's annotation configuration support.
|
||||
<2> Primarily, the configuration comes from the `META-INF/spring/session-server.xml` file, which is also the reason
|
||||
why _Spring Boot_ was not used in this sample, since using XML seemingly defeats the purpose and benefits
|
||||
of using Spring Boot. However, this sample is about demonstrating how to use Spring XML to configure
|
||||
the GemFire client and server.
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-xml-configuration,Spring XML Configuration>> created a Spring bean named `springSessionRepositoryFilter`
|
||||
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with
|
||||
a custom implementation that is backed by Spring Session and GemFire.
|
||||
|
||||
In order for our `Filter` to do its magic, we need to instruct Spring to load our `session-client.xml` configuration file.
|
||||
We do this with the following configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/web.xml[tags=context-param]
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener]
|
||||
reads the `contextConfigLocation` context parameter value and picks up our _session-client.xml_ configuration file.
|
||||
|
||||
Finally, we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter`
|
||||
for every request.
|
||||
|
||||
The following snippet performs this last step for us:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy]
|
||||
will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy`
|
||||
is invoked, the `springSessionRepositoryFilter` will be invoked.
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-gemfire-clientserver-xml-sample-app]]
|
||||
== HttpSession with GemFire (Client/Server) using XML Sample Application
|
||||
|
||||
|
||||
=== Running the httpsession-gemfire-clientserver-xml Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following commands.
|
||||
|
||||
First, you need to run the server using:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-clientserver-xml:run [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
Now, in a separate terminal, you can run the client using:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-clientserver-xml:tomcatRun [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/. In this sample, the web application
|
||||
is the client cache and the server is standalone.
|
||||
|
||||
=== Exploring the httpsession-gemfire-clientserver-xml Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _john_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
|
||||
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
|
||||
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
|
||||
|
||||
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
|
||||
at the command-line:
|
||||
|
||||
$ gfsh
|
||||
|
||||
Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
|
||||
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
|
||||
|
||||
....
|
||||
gfsh>connect --jmx-manager=localhost[1099]
|
||||
|
||||
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
|
||||
|
||||
Result : true
|
||||
startCount : 0
|
||||
endCount : 20
|
||||
Rows : 1
|
||||
|
||||
Result
|
||||
------------------------------------
|
||||
70002719-3c54-4c20-82c3-e7faa6b718f3
|
||||
|
||||
NEXT_STEP_NAME : END
|
||||
|
||||
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
|
||||
....
|
||||
|
||||
NOTE: The _GemFire User Guide_ has more detailed instructions on using https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
|
||||
|
||||
Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed.
|
||||
|
||||
Alternatively, you can wait *30 seconds* for the session to timeout (i.e. expire) and refresh the page. Again, the
|
||||
attribute we added should no longer be displayed in the table. However, keep in mind, that by refreshing the page,
|
||||
you will inadvertently create a new (empty) session. If you run the query again, you will also see two session IDs,
|
||||
the new and the old, since GemFire keeps a "tombstone" of the old session around.
|
||||
@@ -0,0 +1,274 @@
|
||||
= Spring Session - HttpSession with GemFire Client/Server (Quick Start)
|
||||
John Blum
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Spring Session to transparently leverage Pivotal GemFire to back a web application's
|
||||
`HttpSession` using Java Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-gemfire-clientserver-java-sample-app,HttpSession with GemFire (Client/Server) Sample Application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before using Spring Session, you must ensure that the required dependencies are included.
|
||||
If you are using Maven, include the following `dependencies` in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-gemfire</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
[[httpsession-spring-java-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
After adding the required dependencies and repository declarations, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession`
|
||||
with an implementation backed by Spring Session and GemFire.
|
||||
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/ClientConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableGemFireHttpSession` annotation creates a Spring bean named `springSessionRepositoryFilter` that
|
||||
implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session.
|
||||
In this instance, Spring Session is backed by GemFire.
|
||||
<2> Next, we register a `Properties` bean that allows us to configure certain aspects of the GemFire client cache
|
||||
using https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
|
||||
<3> Then, we configure a `Pool` of client connections to talk to the GemFire Server in our Client/Server topology. In our
|
||||
configuration, we have used sensible settings for timeouts, number of connections and so on. Also, the `Pool` has been
|
||||
configured to connect directly to a server. Learn more about various `Pool` configuration settings from the
|
||||
https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html[PoolFactory API].
|
||||
<4> After configuring a `Pool`, we create an instance of the GemFire client cache using the GemFire `Properties`
|
||||
and `Pool` to communicate with the server and perform cache data access operations.
|
||||
<5> Finally, we include a Spring `BeanPostProcessor` to block the client until our GemFire Server is up and running,
|
||||
listening for and accepting client connections.
|
||||
|
||||
The `gemfireCacheServerReadyBeanPostProcessor` is necessary in order to coordinate the client and server in
|
||||
an automated fashion during testing, but unnecessary in situations where the GemFire cluster is already presently
|
||||
running, such as in production. This `BeanPostProcessor` implements 2 approaches to ensure our server has adequate
|
||||
time to startup.
|
||||
|
||||
The first approach uses a timed wait, checking at periodic intervals to determine whether a client `Socket` connection
|
||||
can be made to the server's `CacheServer` endpoint.
|
||||
|
||||
The second approach uses a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html[ClientMembershipListener]
|
||||
that will be notified when the client has successfully connected to the server. Once a connection has been established,
|
||||
the listener releases the latch that the `BeanPostProcessor` will wait on (up to the specified timeout) in the
|
||||
`postProcessAfterInitialization` callback to block the client. Either one of these approaches are sufficient
|
||||
by themselves, but both are demonstrated here to illustrate how this might work and to give you ideas, or other options
|
||||
in practice.
|
||||
|
||||
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers),
|
||||
it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data
|
||||
to clients about the servers available, load and which servers have the client's data of interest, which is particularly
|
||||
important for single-hop, direct data access. See more details about the https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide].
|
||||
|
||||
NOTE: For more information on configuring _Spring Data GemFire_, refer to the https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
|
||||
|
||||
The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both Spring Session
|
||||
and GemFire out-of-the-box using the following attributes:
|
||||
|
||||
* `maxInactiveIntervalInSeconds` - controls HttpSession idle-timeout expiration (defaults to **30 minutes**).
|
||||
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "_ClusteredSpringSessions_").
|
||||
* `clientRegionShort` - specifies GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies]
|
||||
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
|
||||
(default is `PROXY`).
|
||||
|
||||
NOTE: It is important to note that the GemFire client Region name must match a server Region by the same name if
|
||||
the client Region is a `PROXY` or `CACHING_PROXY`. Names are not required to match if the client Region used to
|
||||
store Spring Sessions is `LOCAL`, however, keep in mind that your session state will not be propagated to the server
|
||||
and you lose all benefits of using GemFire to store and manage distributed, replicated session state information
|
||||
in a cluster.
|
||||
|
||||
NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when a peer-to-peer (P2P) topology,
|
||||
and more specifically, a GemFire peer cache is used.
|
||||
|
||||
=== Server Configuration
|
||||
|
||||
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
|
||||
session state up to the server to manage.
|
||||
|
||||
In this sample, we will use the following GemFire Server Java Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/ServerConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> On the server, we also configure Spring Session using the `@EnableGemFireHttpSession` annotation. For one, this
|
||||
ensures that the Region names on both the client and server match (in this sample, we use the default "_ClusteredSpringSessions_").
|
||||
We have also set the session timeout to **30 seconds**. Later, we will see how this timeout is used.
|
||||
<2> Next, we configure the GemFire Server using GemFire System properties very much like our P2P samples.
|
||||
With the `mcast-port` set to 0 and no `locators` property specified, our server will be standalone. We also allow a
|
||||
JMX client (e.g. _Gfsh_) to connect to our server with the use of the GemFire-specific JMX System properties.
|
||||
<3> Then, we create an instance of the GemFire peer cache using our GemFire System properties.
|
||||
<4> We also setup a GemFire `CacheServer` instance running on **localhost**, listening on port **12480**,
|
||||
to accept our client connection.
|
||||
<5> Finally, we declare a `main` method as an entry point for launching and running our GemFire Server
|
||||
from the command-line.
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-java-configuration,Spring Java Configuration>> created a Spring bean named `springSessionRepositoryFilter`
|
||||
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession`
|
||||
with a custom implementation backed by Spring Session and GemFire.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `ClientConfig` class. We also need to ensure our
|
||||
Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. Fortunately, Spring Session
|
||||
provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both of these steps extremely easy.
|
||||
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
|
||||
This ensures that a Spring bean named `springSessionRepositoryFilter` is registered with our Servlet Container
|
||||
and used for every request.
|
||||
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow Spring to load our `ClientConfig`.
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-gemfire-clientserver-java-sample-app]]
|
||||
== HttpSession with GemFire (Client/Server) Sample Application
|
||||
|
||||
|
||||
=== Running the httpsession-gemfire-clientserver Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following commands.
|
||||
|
||||
First, you need to run the server using:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-clientserver:run [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
Then, in a separate terminal, you run the client using:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-clientserver:tomcatRun [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/. In this sample, the web application
|
||||
is the client cache and the server is standalone.
|
||||
|
||||
=== Exploring the httpsession-gemfire-clientserver Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _john_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
|
||||
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
|
||||
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
|
||||
|
||||
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
|
||||
at the command-line:
|
||||
|
||||
$ gfsh
|
||||
|
||||
Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
|
||||
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
|
||||
|
||||
....
|
||||
gfsh>connect --jmx-manager=localhost[1099]
|
||||
|
||||
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
|
||||
|
||||
Result : true
|
||||
startCount : 0
|
||||
endCount : 20
|
||||
Rows : 1
|
||||
|
||||
Result
|
||||
------------------------------------
|
||||
70002719-3c54-4c20-82c3-e7faa6b718f3
|
||||
|
||||
NEXT_STEP_NAME : END
|
||||
|
||||
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
|
||||
....
|
||||
|
||||
NOTE: The _GemFire User Guide_ has more detailed instructions on using https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
|
||||
|
||||
Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed.
|
||||
|
||||
Alternatively, you can wait **30 seconds** for the session to expire and timeout, and then refresh the page. The attribute
|
||||
we added should no longer be displayed in the table. However, keep in mind, that by refreshing the page, you will inadvertently
|
||||
create a new (empty) session. If you run the query again, you will also see two session IDs, the new and the old,
|
||||
since GemFire keeps a "tombstone" of the old session around.
|
||||
210
docs/src/docs/asciidoc/guides/httpsession-gemfire-p2p-xml.adoc
Normal file
210
docs/src/docs/asciidoc/guides/httpsession-gemfire-p2p-xml.adoc
Normal file
@@ -0,0 +1,210 @@
|
||||
= Spring Session - HttpSession with GemFire P2P using XML (Quick Start)
|
||||
John Blum, Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Pivotal GemFire as a provider in Spring Session to transparently back
|
||||
a web application's `HttpSession` using XML Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-gemfire-p2p-xml-sample-app,HttpSession with GemFire (P2P) using XML Sample Application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before using Spring Session, you must ensure that the required dependencies are included.
|
||||
If you are using Maven, include the following `dependencies` in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-gemfire</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
[[httpsession-spring-xml-configuration]]
|
||||
== Spring XML Configuration
|
||||
|
||||
After adding the required dependencies and repository declarations, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession`
|
||||
with an implementation backed by Spring Session and GemFire.
|
||||
|
||||
Add the following Spring Configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/spring/session.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p-xml/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> We use the combination of `<context:annotation-config/>` and `GemFireHttpSessionConfiguration` because Spring Session
|
||||
does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
|
||||
This creates a Spring bean with the name of `springSessionRepositoryFilter` that implements `Filter`. The filter is what
|
||||
replaces the `HttpSession` with an implementation backed by Spring Session.
|
||||
In this instance, Spring Session is backed by GemFire.
|
||||
<2> Then, we configure a GemFire peer cache using standard GemFire System properties. We give the GemFire data node
|
||||
a name using the `name` property and set `mcast-port` to 0. With the absence of a `locators` property, this data node
|
||||
will be a standalone server. GemFire's `log-level` is set using an application-specific System property
|
||||
(`sample.httpsession.gemfire.log-level`) that a user can specify on the command-line when running this application
|
||||
using either Maven or Gradle (default is "_warning_").
|
||||
<3> Finally, we create an instance of the GemFire peer cache that embeds GemFire in the same JVM process as the running
|
||||
Spring Session sample application.
|
||||
|
||||
TIP: Additionally, we have configured this data node (server) as a GemFire Manager as well using GemFire-specific
|
||||
JMX System properties that enable JMX client (e.g. _Gfsh_) to connect to this running data node.
|
||||
|
||||
NOTE: For more information on configuring _Spring Data GemFire_, refer to the https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-xml-configuration,Spring XML Configuration>> created a Spring bean named `springSessionRepositoryFilter`
|
||||
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with
|
||||
a custom implementation that is backed by Spring Session and GemFire.
|
||||
|
||||
In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration file.
|
||||
We do this with the following configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p-xml/src/main/webapp/WEB-INF/web.xml[tags=context-param]
|
||||
include::{samples-dir}httpsession-gemfire-p2p-xml/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener]
|
||||
reads the `contextConfigLocation` context parameter value and picks up our _session.xml_ configuration file.
|
||||
|
||||
Finally, we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter`
|
||||
for every request.
|
||||
|
||||
The following snippet performs this last step for us:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p-xml/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy]
|
||||
will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy`
|
||||
is invoked, the `springSessionRepositoryFilter` will be invoked.
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-gemfire-p2p-xml-sample-app]]
|
||||
== HttpSession with GemFire (P2P) using XML Sample Application
|
||||
|
||||
|
||||
=== Running the httpsession-gemfire-p2p-xml Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-p2p-xml:tomcatRun [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-gemfire-p2p-xml Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _john_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p-xml/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
|
||||
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
|
||||
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
|
||||
|
||||
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
|
||||
at the command-line:
|
||||
|
||||
$ gfsh
|
||||
|
||||
Then, enter the following into _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
|
||||
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
|
||||
|
||||
....
|
||||
gfsh>connect --jmx-manager=localhost[1099]
|
||||
|
||||
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
|
||||
|
||||
Result : true
|
||||
startCount : 0
|
||||
endCount : 20
|
||||
Rows : 1
|
||||
|
||||
Result
|
||||
------------------------------------
|
||||
70002719-3c54-4c20-82c3-e7faa6b718f3
|
||||
|
||||
NEXT_STEP_NAME : END
|
||||
|
||||
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
|
||||
....
|
||||
|
||||
NOTE: The _GemFire User Guide_ has more detailed instructions on using https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
209
docs/src/docs/asciidoc/guides/httpsession-gemfire-p2p.adoc
Normal file
209
docs/src/docs/asciidoc/guides/httpsession-gemfire-p2p.adoc
Normal file
@@ -0,0 +1,209 @@
|
||||
= Spring Session - HttpSession with GemFire P2P (Quick Start)
|
||||
John Blum, Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Pivotal GemFire as a provider in Spring Session to transparently back
|
||||
a web application's `HttpSession` using Java Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-gemfire-p2p-java-sample-app,HttpSession with GemFire (P2P) Sample Application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before using Spring Session, you must ensure that the required dependencies are included.
|
||||
If you are using Maven, include the following `dependencies` in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-gemfire</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
|
||||
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
[[httpsession-spring-java-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
After adding the required dependencies and repository declarations, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession`
|
||||
with an implementation backed by Spring Session and GemFire.
|
||||
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p/src/main/java/sample/Config.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableGemFireHttpSession` annotation creates a Spring bean named `springSessionRepositoryFilter` that
|
||||
implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session.
|
||||
In this instance, Spring Session is backed by GemFire.
|
||||
<2> Then, we configure a GemFire peer cache using standard GemFire System properties. We give the GemFire data node
|
||||
a name using the `name` property and set `mcast-port` to 0. With the absence of a `locators` property, this data node
|
||||
will be a standalone server. GemFire's `log-level` is set using an application-specific System property (`sample.httpsession.gemfire.log-level`)
|
||||
that a user can specify on the command-line when running this sample application using either Maven or Gradle (default is "_warning_").
|
||||
<3> Finally, we create an instance of the GemFire peer cache that embeds GemFire in the same JVM process as the running
|
||||
Spring Session sample application.
|
||||
|
||||
TIP: Additionally, we have configured this data node (server) as a GemFire Manager as well using GemFire-specific
|
||||
JMX System properties that enable JMX client (e.g. _Gfsh_) to connect to this running data node.
|
||||
|
||||
NOTE: For more information on configuring _Spring Data GemFire_, refer to the https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
|
||||
|
||||
The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of Spring Session
|
||||
and GemFire out-of-the-box using the following attributes:
|
||||
|
||||
* `maxInactiveIntervalInSeconds` - controls HttpSession idle-timeout expiration (defaults to **30 minutes**).
|
||||
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "_ClusteredSpringSessions_").
|
||||
* `serverRegionShort` - specifies GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies]
|
||||
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
|
||||
(default is `PARTITION`).
|
||||
|
||||
NOTE: `clientRegionShort` is ignored in a peer cache configuration and only applies when a client-server topology,
|
||||
and more specifically, a GemFire client cache is used.
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-java-configuration,Spring Java Configuration>> created a Spring bean named `springSessionRepositoryFilter`
|
||||
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession`
|
||||
with a custom implementation backed by Spring Session and GemFire.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class. We also need to ensure our
|
||||
Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. Fortunately, Spring Session
|
||||
provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both of these steps extremely easy.
|
||||
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
|
||||
This ensures that a Spring bean named `springSessionRepositoryFilter` is registered with our Servlet Container
|
||||
and used for every request.
|
||||
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow Spring to load our `Config`.
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-gemfire-p2p-java-sample-app]]
|
||||
== HttpSession with GemFire (P2P) Sample Application
|
||||
|
||||
|
||||
=== Running the httpsession-gemfire-p2p Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-gemfire-p2p:tomcatRun [-Dsample.httpsession.gemfire.log-level=info]
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-gemfire-p2p Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _john_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-gemfire-p2p/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
|
||||
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
|
||||
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
|
||||
|
||||
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
|
||||
at the command-line:
|
||||
|
||||
$ gfsh
|
||||
|
||||
Then, enter the following into _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
|
||||
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
|
||||
|
||||
....
|
||||
gfsh>connect --jmx-manager=localhost[1099]
|
||||
|
||||
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
|
||||
|
||||
Result : true
|
||||
startCount : 0
|
||||
endCount : 20
|
||||
Rows : 1
|
||||
|
||||
Result
|
||||
------------------------------------
|
||||
70002719-3c54-4c20-82c3-e7faa6b718f3
|
||||
|
||||
NEXT_STEP_NAME : END
|
||||
|
||||
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
|
||||
....
|
||||
|
||||
NOTE: The _GemFire User Guide_ has more detailed instructions on using https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
152
docs/src/docs/asciidoc/guides/httpsession-jdbc-boot.adoc
Normal file
152
docs/src/docs/asciidoc/guides/httpsession-jdbc-boot.adoc
Normal file
@@ -0,0 +1,152 @@
|
||||
= Spring Session - Spring Boot
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` when using Spring Boot.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-jdbc-boot-sample, httpsession-jdbc-boot sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
We assume you are working with a working Spring Boot web application.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-jdbc-boot-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-jdbc-boot/src/main/java/sample/config/HttpSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableJdbcHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by a relational database.
|
||||
|
||||
[[httpsession-jdbc-boot-configuration]]
|
||||
== Configuring the DataSource
|
||||
|
||||
Spring Boot automatically creates a `DataSource` that connects Spring Session to an embedded instance of H2 database.
|
||||
In a production environment you need to ensure to update your configuration to point to your relational database.
|
||||
For example, you can include the following in your *application.properties*
|
||||
|
||||
.src/main/resources/application.properties
|
||||
----
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/myapp
|
||||
spring.datasource.username=myapp
|
||||
spring.datasource.password=secret
|
||||
----
|
||||
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-configure-datasource[Configure a DataSource] portion of the Spring Boot documentation.
|
||||
|
||||
[[httpsession-jdbc-boot-servlet-configuration]]
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-jdbc-boot-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Boot takes care of both of these steps for us.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-jdbc-boot-sample]]
|
||||
== httpsession-jdbc-boot Sample Application
|
||||
|
||||
The httpsession-jdbc-boot Sample Application demonstrates how to use Spring Session to transparently leverage H2 database to back a web application's `HttpSession` when using Spring Boot.
|
||||
|
||||
[[httpsession-jdbc-boot-running]]
|
||||
=== Running the httpsession-jdbc-boot Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc-boot:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
[[httpsession-jdbc-boot-explore]]
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in H2 database rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
[[httpsession-jdbc-boot-how]]
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by a relational database.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into H2 database.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL)
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
162
docs/src/docs/asciidoc/guides/httpsession-jdbc-xml.adoc
Normal file
162
docs/src/docs/asciidoc/guides/httpsession-jdbc-xml.adoc
Normal file
@@ -0,0 +1,162 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational to back a web application's `HttpSession` with XML based configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-jdbc-xml-sample, httpsession-jdbc-xml sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-jdbc-xml-spring-configuration]]
|
||||
== Spring XML Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/spring/session.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-jdbc-xml/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> We use the combination of `<context:annotation-config/>` and `JdbcHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
|
||||
This creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by a relational database.
|
||||
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
|
||||
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
|
||||
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
|
||||
|
||||
For additional information on how to configure data access related concerns, please refer to the https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-xml-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration.
|
||||
We do this with the following configuration:
|
||||
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=context-param]
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
The following snippet performs this last step for us:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`.
|
||||
For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-jdbc-xml-sample]]
|
||||
== httpsession-jdbc-xml Sample Application
|
||||
|
||||
=== Running the httpsession-jdbc-xml Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc-xml:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-jdbc-xml Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-jdbc-xml/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL)
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
152
docs/src/docs/asciidoc/guides/httpsession-jdbc.adoc
Normal file
152
docs/src/docs/asciidoc/guides/httpsession-jdbc.adoc
Normal file
@@ -0,0 +1,152 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` with Java Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-jdbc-sample, httpsession-jdbc sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-jdbc-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-jdbc/src/main/java/sample/Config.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableJdbcHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by a relational database.
|
||||
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
|
||||
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
|
||||
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
|
||||
|
||||
For additional information on how to configure data access related concerns, please refer to the https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` both of these steps extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
|
||||
This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request.
|
||||
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily ensure Spring loads our `Config`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-jdbc-sample]]
|
||||
== httpsession-jdbc Sample Application
|
||||
|
||||
=== Running the httpsession-jdbc Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-jdbc Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-jdbc/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL)
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
174
docs/src/docs/asciidoc/guides/httpsession-xml.adoc
Normal file
174
docs/src/docs/asciidoc/guides/httpsession-xml.adoc
Normal file
@@ -0,0 +1,174 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with XML based configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-xml-sample, httpsession-xml sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-xml-spring-configuration]]
|
||||
== Spring XML Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/spring/session.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> We use the combination of `<context:annotation-config/>` and `RedisHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
|
||||
This creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
|
||||
We configure the connection to connect to localhost on the default port (6379)
|
||||
For more information on configuring Spring Data Redis, refer to the https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-xml-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration.
|
||||
We do this with the following configuration:
|
||||
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=context-param]
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
The following snippet performs this last step for us:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`.
|
||||
For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-xml-sample]]
|
||||
== httpsession-xml Sample Application
|
||||
|
||||
=== Running the httpsession-xml Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-xml:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-xml Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession-xml/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
166
docs/src/docs/asciidoc/guides/httpsession.adoc
Normal file
166
docs/src/docs/asciidoc/guides/httpsession.adoc
Normal file
@@ -0,0 +1,166 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with Java Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-sample, httpsession sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession/src/main/java/sample/Config.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
|
||||
We configure the connection to connect to localhost on the default port (6379)
|
||||
For more information on configuring Spring Data Redis, refer to the https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` both of these steps extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
|
||||
This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request.
|
||||
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily ensure Spring loads our `Config`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-sample]]
|
||||
== httpsession Sample Application
|
||||
|
||||
|
||||
|
||||
=== Running the httpsession Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}httpsession/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
169
docs/src/docs/asciidoc/guides/mongo.adoc
Normal file
169
docs/src/docs/asciidoc/guides/mongo.adoc
Normal file
@@ -0,0 +1,169 @@
|
||||
= Spring Session - Mongo Repositories
|
||||
Jakub Kubrynski
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session backed by Mongo.
|
||||
|
||||
NOTE: The completed guide can be found in the <<mongo-sample, mongo sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
We assume you are working with a working Spring Boot web application.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-mongo</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[mongo-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
|
||||
// tag::config[]
|
||||
All you have to do is to add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}mongo/src/main/java/sample/config/HttpSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableMongoHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Mongo.
|
||||
|
||||
<2> We explicitly configure `JdkMongoSessionConverter` since Spring Security's objects cannot be automatically persisted using Jackson (the default if Jackson is on the classpath).
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[boot-mongo-configuration]]
|
||||
== Configuring the Mongo Connection
|
||||
|
||||
Spring Boot automatically creates a `MongoClient` that connects Spring Session to a Mongo Server on localhost on port 27017 (default port).
|
||||
In a production environment you need to ensure to update your configuration to point to your Mongo server.
|
||||
For example, you can include the following in your *application.properties*
|
||||
|
||||
.src/main/resources/application.properties
|
||||
----
|
||||
spring.data.mongodb.host=mongo-srv
|
||||
spring.data.mongodb.port=27018
|
||||
spring.data.mongodb.database=prod
|
||||
----
|
||||
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connecting-to-mongodb[Connecting to MongoDB] portion of the Spring Boot documentation.
|
||||
|
||||
[[boot-servlet-configuration]]
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<boot-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Boot takes care of both of these steps for us.
|
||||
|
||||
[[mongo-sample]]
|
||||
== Mongo Sample Application
|
||||
|
||||
The Mongo Sample Application demonstrates how to use Spring Session to transparently leverage Mongo to back a web application's `HttpSession` when using Spring Boot.
|
||||
|
||||
[[mongo-running]]
|
||||
=== Running the Mongo Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:mongo:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
[[boot-explore]]
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Mongo rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
[[mongo-how]]
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Mongo.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by Mongo.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Mongo.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily inspect the session using mongo client. For example, on a Linux based system you can type:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The sample application uses an embedded MongoDB instance that listens on a randomly allocated port.
|
||||
The port used by embedded MongoDB together with exact command to connect to it is logged during application startup.
|
||||
====
|
||||
|
||||
$ mongo --port ...
|
||||
> use test
|
||||
> db.sessions.find().pretty()
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `60f17293-839b-477c-bb92-07a9c3658843` with the value of your SESSION cookie:
|
||||
|
||||
> db.sessions.remove({"_id":"60f17293-839b-477c-bb92-07a9c3658843"})
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
214
docs/src/docs/asciidoc/guides/rest.adoc
Normal file
214
docs/src/docs/asciidoc/guides/rest.adoc
Normal file
@@ -0,0 +1,214 @@
|
||||
= Spring Session - REST
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using REST endpoints.
|
||||
|
||||
NOTE: The completed guide can be found in the <<rest-sample, rest sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[rest-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}rest/src/main/java/sample/HttpSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
|
||||
We configure the connection to connect to localhost on the default port (6379)
|
||||
For more information on configuring Spring Data Redis, refer to the https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
<3> We customize Spring Session's HttpSession integration to use HTTP headers to convey the current session information instead of cookies.
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<rest-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class. We provide the configuration in our Spring `MvcInitializer` as shown below:
|
||||
|
||||
.src/main/java/sample/mvc/MvcInitializer.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}rest/src/main/java/sample/mvc/MvcInitializer.java[tags=config]
|
||||
----
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy. Simply extend the class with the default constructor as shown below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}rest/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[rest-sample]]
|
||||
== rest Sample Application
|
||||
|
||||
=== Running the rest Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:rest:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the rest Sample Application
|
||||
|
||||
Try using the application. Use your favorite REST client to request http://localhost:8080/
|
||||
|
||||
$ curl -v http://localhost:8080/
|
||||
|
||||
Observe that we are prompted for basic authentication. Provide the following information for the username and password:
|
||||
|
||||
* **Username** *user*
|
||||
* **Password** *password*
|
||||
|
||||
$ curl -v http://localhost:8080/ -u user:password
|
||||
|
||||
In the output you will notice the following:
|
||||
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3
|
||||
|
||||
{"username":"user"}
|
||||
----
|
||||
|
||||
Specifically, we notice the following things about our response:
|
||||
|
||||
* The HTTP Status is now a 200
|
||||
* We have a header with the name of *x-auth-token* which contains a new session id
|
||||
* The current username is displayed
|
||||
|
||||
We can now use the *x-auth-token* to make another request without providing the username and password again. For example, the following outputs the username just as before:
|
||||
|
||||
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
|
||||
The only difference is that the session id is not provided in the response headers because we are reusing an existing session.
|
||||
|
||||
If we invalidate the session, then the x-auth-token is displayed in the response with an empty value. For example, the following will invalidate our session:
|
||||
|
||||
$ curl -v http://localhost:8080/logout -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
|
||||
You will see in the output that the x-auth-token provides an empty String indicating that the previous session was invalidated.
|
||||
|
||||
----
|
||||
HTTP/1.1 204 No Content
|
||||
...
|
||||
x-auth-token:
|
||||
----
|
||||
|
||||
=== How does it work?
|
||||
|
||||
Spring Security interacts with the standard `HttpSession` in `SecurityContextPersistenceFilter`.
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, Spring Security is now persisting the values in Redis.
|
||||
Spring Session creates a header named x-auth-token in your browser that contains the id of your session.
|
||||
|
||||
If you like, you can easily see that the session is created in Redis. First create a session using the following:
|
||||
|
||||
$ curl -v http://localhost:8080/ -u user:password
|
||||
|
||||
In the output you will notice the following:
|
||||
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
x-auth-token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
{"username":"user"}
|
||||
----
|
||||
|
||||
Now remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
We can now use the *x-auth-token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401:
|
||||
|
||||
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
168
docs/src/docs/asciidoc/guides/security.adoc
Normal file
168
docs/src/docs/asciidoc/guides/security.adoc
Normal file
@@ -0,0 +1,168 @@
|
||||
= Spring Session and Spring Security
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session along with Spring Security.
|
||||
It assumes you have already applied Spring Security to your application.
|
||||
|
||||
NOTE: The completed guide can be found in the <<security-sample, security sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[security-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}security/src/main/java/sample/Config.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
|
||||
We configure the connection to connect to localhost on the default port (6379)
|
||||
For more information on configuring Spring Data Redis, refer to the https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<security-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our Config class to it.
|
||||
|
||||
.src/main/java/sample/SecurityInitializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}security/src/main/java/sample/SecurityInitializer.java[tags=class]
|
||||
----
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
It is extremely important that Spring Session's `springSessionRepositoryFilter` is invoked before Spring Security's `springSecurityFilterChain`.
|
||||
This ensures that the `HttpSession` that Spring Security uses is backed by Spring Session.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}security/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request before Spring Security's `springSecurityFilterChain` .
|
||||
|
||||
[[security-sample]]
|
||||
== security Sample Application
|
||||
|
||||
|
||||
|
||||
=== Running the security Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:security:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Redis rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by Redis.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Redis.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
161
docs/src/docs/asciidoc/guides/users.adoc
Normal file
161
docs/src/docs/asciidoc/guides/users.adoc
Normal file
@@ -0,0 +1,161 @@
|
||||
= Spring Session - Multiple Sessions
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts).
|
||||
|
||||
== Integrating with Spring Session
|
||||
|
||||
The steps to integrate with Spring Session are exactly the same as those outline in the link:httpsession.html[HttpSession Guide], so we will skip to running the sample application.
|
||||
|
||||
[[users-sample]]
|
||||
== users Sample Application
|
||||
|
||||
The users application demonstrates how to allow an application to manage multiple simultaneous browser sessions (i.e. Google Accounts).
|
||||
|
||||
=== Running the users Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:users:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the users Sample Application
|
||||
|
||||
Try using the application. Authenticate with the following information:
|
||||
|
||||
* **Username** _rob_
|
||||
* **Password** _rob_
|
||||
|
||||
Now click the **Login** button. You should now be authenticated as the user **rob**.
|
||||
|
||||
We can click on links and our user information is preserved.
|
||||
|
||||
* Click on the **Link** link in the navigation bar at the top
|
||||
* Observe we are still authenticated as **rob**
|
||||
|
||||
Let's add an another account.
|
||||
|
||||
* Return to the *Home* page
|
||||
* Click on the arrow next to *rob* in the upper right hand corner
|
||||
* Click **Add Account**
|
||||
|
||||
The log in form is displayed again. Authenticate with the following information:
|
||||
|
||||
* **Username** _luke_
|
||||
* **Password** _luke_
|
||||
|
||||
Now click the **Login** button. You should now be authenticated as the user **luke**.
|
||||
|
||||
We can click on links and our user information is preserved.
|
||||
|
||||
* Click on the **Link** link in the navigation bar at the top
|
||||
* Observe we are still authenticated as **luke**
|
||||
|
||||
Where did our original user go? Let's switch to our original account.
|
||||
|
||||
* Click on the arrow next to *luke* in the upper right hand corner.
|
||||
* Click on **Switch Account** -> *rob*
|
||||
|
||||
We are now using the session associated with *rob*.
|
||||
|
||||
== How does it work?
|
||||
|
||||
// tag::how-does-it-work[]
|
||||
|
||||
Let's take a look at how Spring Session keeps track of multiple sessions.
|
||||
|
||||
=== Managing a Single Session
|
||||
|
||||
Spring Session keeps track of the `HttpSession` by adding a value to a cookie named SESSION.
|
||||
For example, the SESSION cookie might have a value of:
|
||||
|
||||
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
=== Adding a Session
|
||||
|
||||
We can add another session by requesting a URL that contains a special parameter in it.
|
||||
By default the parameter name is *_s*. For example, the following URL would create a new session:
|
||||
|
||||
http://localhost:8080/?_s=1
|
||||
|
||||
NOTE: The parameter value does not indicate the actual session id.
|
||||
This is important because we never want to allow the session id to be determined by a client to avoid https://www.owasp.org/index.php/Session_fixation[session fixation attacks].
|
||||
Additionally, we do not want the session id to be leaked since it is sent as a query parameter.
|
||||
Remember sensitive information should only be transmitted as a header or in the body of the request.
|
||||
|
||||
Rather than creating the URL ourselves, we can utilize the `HttpSessionManager` to do this for us.
|
||||
We can obtain the `HttpSessionManager` from the `HttpServletRequest` using the following:
|
||||
|
||||
.src/main/java/sample/UserAccountsFilter.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}users/src/main/java/sample/UserAccountsFilter.java[tags=HttpSessionManager]
|
||||
----
|
||||
|
||||
We can now use it to create a URL to add another session.
|
||||
|
||||
.src/main/java/sample/UserAccountsFilter.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}users/src/main/java/sample/UserAccountsFilter.java[tags=addAccountUrl]
|
||||
----
|
||||
|
||||
<1> We have an existing variable named `unauthenticatedAlias`.
|
||||
The value is an alias that points to an existing unauthenticated session.
|
||||
If no such session exists, the value is null.
|
||||
This ensures if we have an existing unauthenticated session that we use it instead of creating a new session.
|
||||
<2> If all of our sessions are already associated to a user, we create a new session alias.
|
||||
<3> If there is an existing session that is not associated to a user, we use its session alias.
|
||||
<4> Finally, we create the add account URL.
|
||||
The URL contains a session alias that either points to an existing unauthenticated session or is an alias that is unused thus signaling to create a new session associated to that alias.
|
||||
|
||||
Now our SESSION cookie looks something like this:
|
||||
|
||||
0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad
|
||||
|
||||
Such that:
|
||||
|
||||
* There is a session with the id *7e8383a4-082c-4ffe-a4bc-c40fd3363c5e*
|
||||
** The alias for this session is *0*.
|
||||
For example, if the URL is http://localhost:8080/?_s=0 this alias would be used.
|
||||
** This is the default session.
|
||||
This means that if no session alias is specified, then this session is used.
|
||||
For example, if the URL is http://localhost:8080/ this session would be used.
|
||||
* There is a session with the id *1d526d4a-c462-45a4-93d9-84a39b6d44ad*
|
||||
** The alias for this session is *1*.
|
||||
If the session alias is *1*, then this session is used.
|
||||
For example, if the URL is http://localhost:8080/?_s=1 this alias would be used.
|
||||
|
||||
=== Automatic Session Alias Inclusion with encodeURL
|
||||
|
||||
The nice thing about specifying the session alias in the URL is that we can have multiple tabs open with different active sessions.
|
||||
The bad thing is that we need to include the session alias in every URL of our application.
|
||||
Fortunately, Spring Session will automatically include the session alias in any URL that passes through https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#encodeURL(java.lang.String)[HttpServletResponse#encodeURL(java.lang.String)]
|
||||
|
||||
This means that if you are using standard tag libraries the session alias is automatically included in the URL.
|
||||
For example, if we are currently using the session with the alias of *1*, then the following:
|
||||
|
||||
.src/main/webapp/index.jsp
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}users/src/main/webapp/index.jsp[tags=link]
|
||||
----
|
||||
|
||||
will output a link of:
|
||||
|
||||
[source,html]
|
||||
----
|
||||
<a id="navLink" href="/link.jsp?_s=1">Link</a>
|
||||
----
|
||||
|
||||
// end::how-does-it-work[]
|
||||
138
docs/src/docs/asciidoc/guides/websocket.adoc
Normal file
138
docs/src/docs/asciidoc/guides/websocket.adoc
Normal file
@@ -0,0 +1,138 @@
|
||||
= Spring Session - WebSocket
|
||||
Rob Winch
|
||||
:toc:
|
||||
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
|
||||
|
||||
This guide describes how to use Spring Session to ensure that WebSocket messages keep your HttpSession alive.
|
||||
|
||||
// tag::disclaimer[]
|
||||
|
||||
NOTE: Spring Session's WebSocket support only works with Spring's WebSocket support.
|
||||
Specifically it does not work with using https://www.jcp.org/en/jsr/detail?id=356[JSR-356] directly.
|
||||
This is due to the fact that JSR-356 does not have a mechanism for intercepting incoming WebSocket messages.
|
||||
|
||||
// end::disclaimer[]
|
||||
|
||||
== HttpSession Setup
|
||||
|
||||
The first step is to integrate Spring Session with the HttpSession. These steps are already outlined in the link:httpsession.html[HttpSession Guide].
|
||||
|
||||
Please make sure you have already integrated Spring Session with the HttpSession before proceeding.
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[websocket-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
In a typical Spring WebSocket application users would extend `AbstractWebSocketMessageBrokerConfigurer`.
|
||||
For example, the configuration might look something like the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{websocketdoc-test-dir}WebSocketConfig.java[tags=class]
|
||||
----
|
||||
|
||||
We can easily update our configuration to use Spring Session's WebSocket support.
|
||||
For example:
|
||||
|
||||
.src/main/java/samples/config/WebSocketConfig.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}websocket/src/main/java/sample/config/WebSocketConfig.java[tags=class]
|
||||
----
|
||||
|
||||
To hook in the Spring Session support we only need to change two things:
|
||||
|
||||
<1> Instead of extending `AbstractWebSocketMessageBrokerConfigurer` we extend `AbstractSessionWebSocketMessageBrokerConfigurer`
|
||||
<2> We rename the `registerStompEndpoints` method to `configureStompEndpoints`
|
||||
|
||||
What does `AbstractSessionWebSocketMessageBrokerConfigurer` do behind the scenes?
|
||||
|
||||
* `WebSocketConnectHandlerDecoratorFactory` is added as a `WebSocketHandlerDecoratorFactory` to `WebSocketTransportRegistration`.
|
||||
This ensures a custom `SessionConnectEvent` is fired that contains the `WebSocketSession`.
|
||||
The `WebSocketSession` is necessary to terminate any WebSocket connections that are still open when a Spring Session is terminated.
|
||||
* `SessionRepositoryMessageInterceptor` is added as a `HandshakeInterceptor` to every `StompWebSocketEndpointRegistration`.
|
||||
This ensures that the Session is added to the WebSocket properties to enable updating the last accessed time.
|
||||
* `SessionRepositoryMessageInterceptor` is added as a `ChannelInterceptor` to our inbound `ChannelRegistration`.
|
||||
This ensures that every time an inbound message is received, that the last accessed time of our Spring Session is updated.
|
||||
* `WebSocketRegistryListener` is created as a Spring Bean.
|
||||
This ensures that we have a mapping of all of the Session id to the corresponding WebSocket connections.
|
||||
By maintaining this mapping, we can close all the WebSocket connections when a Spring Session (HttpSession) is terminated.
|
||||
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[websocket-sample]]
|
||||
== websocket Sample Application
|
||||
|
||||
The websocket sample application demonstrates how to use Spring Session with WebSockets.
|
||||
|
||||
=== Running the websocket Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[TIP]
|
||||
====
|
||||
For the purposes of testing session expiration, you may want to change the session expiration to be 1 minute (default is 30 minutes) by removing the comment from the following file before starting the application:
|
||||
|
||||
.src/main/java/samples/config/WebSecurityConfig.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}websocket/src/main/java/sample/config/WebSecurityConfig.java[tags=enable-redis-httpsession]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:websocket:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the websocket Sample Application
|
||||
|
||||
Try using the application. Authenticate with the following information:
|
||||
|
||||
* **Username** _rob_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button. You should now be authenticated as the user **rob**.
|
||||
|
||||
Open an incognito window and access http://localhost:8080/
|
||||
|
||||
You will be prompted with a log in form. Authenticate with the following information:
|
||||
|
||||
* **Username** _luke_
|
||||
* **Password** _password_
|
||||
|
||||
Now send a message from *rob* to *luke*. The message should appear.
|
||||
|
||||
Wait for two minutes and try sending a message from *rob* to *luke* again.
|
||||
You will see that the message is no longer sent.
|
||||
|
||||
[NOTE]
|
||||
.Why two minutes?
|
||||
====
|
||||
Spring Session will expire in 60 seconds, but the notification from Redis is not guaranteed to happen within 60 seconds.
|
||||
To ensure the socket is closed in a reasonable amount of time, Spring Session runs a background task every minute at 00 seconds that forcibly cleans up any expired sessions.
|
||||
This means you will need to wait at most two minutes before the WebSocket connection is terminated.
|
||||
====
|
||||
|
||||
Try accessing http://localhost:8080/
|
||||
You will be prompted to authenticate again.
|
||||
This demonstrates that the session properly expires.
|
||||
|
||||
Now repeat the same exercise, but instead of waiting two minutes send a message from *each* of the users every 30 seconds.
|
||||
You will see that the messages continue to be sent.
|
||||
Try accessing http://localhost:8080/
|
||||
You will not be prompted to authenticate again.
|
||||
This demonstrates the session is kept alive.
|
||||
|
||||
NOTE: Only messages sent from a user keep the session alive.
|
||||
This is because only messages coming from a user imply user activity.
|
||||
Messages received do not imply activity and thus do not renew the session expiration.
|
||||
1116
docs/src/docs/asciidoc/index.adoc
Normal file
1116
docs/src/docs/asciidoc/index.adoc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.CacheClosedException;
|
||||
import com.gemstone.gemfire.cache.DataPolicy;
|
||||
import com.gemstone.gemfire.cache.ExpirationAction;
|
||||
import com.gemstone.gemfire.cache.ExpirationAttributes;
|
||||
import com.gemstone.gemfire.cache.GemFireCache;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.client.ClientCache;
|
||||
import com.gemstone.gemfire.cache.client.ClientCacheFactory;
|
||||
import com.gemstone.gemfire.cache.query.Index;
|
||||
import com.gemstone.gemfire.cache.server.CacheServer;
|
||||
import org.junit.Before;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* AbstractGemFireIntegrationTests is an abstract base class encapsulating common
|
||||
* operations for writing Spring Session GemFire integration tests.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.1.0
|
||||
* @see org.springframework.session.ExpiringSession
|
||||
* @see org.springframework.session.events.AbstractSessionEvent
|
||||
* @see com.gemstone.gemfire.cache.Cache
|
||||
* @see com.gemstone.gemfire.cache.DataPolicy
|
||||
* @see com.gemstone.gemfire.cache.ExpirationAttributes
|
||||
* @see com.gemstone.gemfire.cache.GemFireCache
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @see com.gemstone.gemfire.cache.client.ClientCache
|
||||
* @see com.gemstone.gemfire.cache.server.CacheServer
|
||||
*/
|
||||
public class AbstractGemFireIntegrationTests {
|
||||
public static final String GEMFIRE_LOG_LEVEL = System
|
||||
.getProperty("spring.session.data.gemfire.log-level", "warning");
|
||||
|
||||
protected static final boolean DEFAULT_ENABLE_QUERY_DEBUGGING = false;
|
||||
protected static final boolean GEMFIRE_QUERY_DEBUG = Boolean
|
||||
.getBoolean("spring.session.data.gemfire.query.debug");
|
||||
|
||||
protected static final int DEFAULT_GEMFIRE_SERVER_PORT = CacheServer.DEFAULT_PORT;
|
||||
|
||||
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
|
||||
protected static final long DEFAULT_WAIT_INTERVAL = 500L;
|
||||
|
||||
protected static final File WORKING_DIRECTORY = new File(
|
||||
System.getProperty("user.dir"));
|
||||
|
||||
protected static final String DEFAULT_PROCESS_CONTROL_FILENAME = "process.ctl";
|
||||
|
||||
protected static final String GEMFIRE_LOG_FILE_NAME = System
|
||||
.getProperty("spring.session.data.gemfire.log-file", "server.log");
|
||||
|
||||
@Autowired
|
||||
protected Cache gemfireCache;
|
||||
|
||||
@Autowired
|
||||
protected GemFireOperationsSessionRepository sessionRepository;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
System.setProperty("gemfire.Query.VERBOSE",
|
||||
String.valueOf(isQueryDebuggingEnabled()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(String pathname) {
|
||||
File directory = new File(WORKING_DIRECTORY, pathname);
|
||||
|
||||
assertThat(directory.isDirectory() || directory.mkdirs())
|
||||
.as(String.format("Failed to create directory (%1$s)", directory))
|
||||
.isTrue();
|
||||
|
||||
directory.deleteOnExit();
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static List<String> createJavaProcessCommandLine(Class<?> type,
|
||||
String... args) {
|
||||
List<String> commandLine = new ArrayList<String>();
|
||||
|
||||
String javaHome = System.getProperty("java.home");
|
||||
String javaExe = new File(new File(javaHome, "bin"), "java").getAbsolutePath();
|
||||
|
||||
commandLine.add(javaExe);
|
||||
commandLine.add("-server");
|
||||
commandLine.add("-ea");
|
||||
commandLine.add(String.format("-Dgemfire.log-file=%1$s", GEMFIRE_LOG_FILE_NAME));
|
||||
commandLine.add(String.format("-Dgemfire.log-level=%1$s", GEMFIRE_LOG_LEVEL));
|
||||
commandLine
|
||||
.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
|
||||
commandLine.addAll(extractJvmArguments(args));
|
||||
commandLine.add("-classpath");
|
||||
commandLine.add(System.getProperty("java.class.path"));
|
||||
commandLine.add(type.getName());
|
||||
commandLine.addAll(extractProgramArguments(args));
|
||||
|
||||
// System.err.printf("Java process command-line is (%1$s)%n", commandLine);
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static List<String> extractJvmArguments(final String... args) {
|
||||
List<String> jvmArgs = new ArrayList<String>(args.length);
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("-")) {
|
||||
jvmArgs.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return jvmArgs;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static List<String> extractProgramArguments(final String... args) {
|
||||
List<String> jvmArgs = new ArrayList<String>(args.length);
|
||||
|
||||
for (String arg : args) {
|
||||
if (!arg.startsWith("-")) {
|
||||
jvmArgs.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return jvmArgs;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static Process run(Class<?> type, File directory, String... args)
|
||||
throws IOException {
|
||||
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args))
|
||||
.directory(directory).start();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer) {
|
||||
return waitForCacheServerToStart(cacheServer, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer,
|
||||
long duration) {
|
||||
return waitForCacheServerToStart(cacheServer.getBindAddress(),
|
||||
cacheServer.getPort(), duration);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(String host, int port) {
|
||||
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(final String host, final int port,
|
||||
long duration) {
|
||||
return waitOnCondition(new Condition() {
|
||||
AtomicBoolean connected = new AtomicBoolean(false);
|
||||
|
||||
public boolean evaluate() {
|
||||
Socket socket = null;
|
||||
|
||||
try {
|
||||
if (!connected.get()) {
|
||||
socket = new Socket(host, port);
|
||||
connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
GemFireUtils.close(socket);
|
||||
}
|
||||
|
||||
return connected.get();
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
// NOTE this method would not be necessary except Spring Sessions' build does not fork
|
||||
// the test JVM
|
||||
// for every test class.
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForClientCacheToClose() {
|
||||
return waitForClientCacheToClose(DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForClientCacheToClose(long duration) {
|
||||
try {
|
||||
final ClientCache clientCache = ClientCacheFactory.getAnyInstance();
|
||||
|
||||
clientCache.close();
|
||||
|
||||
waitOnCondition(new Condition() {
|
||||
public boolean evaluate() {
|
||||
return clientCache.isClosed();
|
||||
}
|
||||
}, duration);
|
||||
|
||||
return clientCache.isClosed();
|
||||
}
|
||||
catch (CacheClosedException ignore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForProcessToStart(Process process, File directory) {
|
||||
return waitForProcessToStart(process, directory, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected static boolean waitForProcessToStart(Process process, File directory,
|
||||
long duration) {
|
||||
final File processControl = new File(directory, DEFAULT_PROCESS_CONTROL_FILENAME);
|
||||
|
||||
waitOnCondition(new Condition() {
|
||||
public boolean evaluate() {
|
||||
return processControl.isFile();
|
||||
}
|
||||
}, duration);
|
||||
|
||||
return process.isAlive();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int waitForProcessToStop(Process process, File directory) {
|
||||
return waitForProcessToStop(process, directory, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int waitForProcessToStop(Process process, File directory,
|
||||
long duration) {
|
||||
final long timeout = (System.currentTimeMillis() + duration);
|
||||
|
||||
try {
|
||||
while (process.isAlive() && System.currentTimeMillis() < timeout) {
|
||||
if (process.waitFor(DEFAULT_WAIT_INTERVAL, TimeUnit.MILLISECONDS)) {
|
||||
return process.exitValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
return (process.isAlive() ? -1 : process.exitValue());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitOnCondition(Condition condition) {
|
||||
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected static boolean waitOnCondition(Condition condition, long duration) {
|
||||
final long timeout = (System.currentTimeMillis() + duration);
|
||||
|
||||
try {
|
||||
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
|
||||
synchronized (condition) {
|
||||
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
return condition.evaluate();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File writeProcessControlFile(File path) throws IOException {
|
||||
assertThat(path != null && path.isDirectory()).isTrue();
|
||||
|
||||
File processControl = new File(path, DEFAULT_PROCESS_CONTROL_FILENAME);
|
||||
|
||||
assertThat(processControl.createNewFile()).isTrue();
|
||||
|
||||
processControl.deleteOnExit();
|
||||
|
||||
return processControl;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertRegion(Region<?, ?> actualRegion, String expectedName,
|
||||
DataPolicy expectedDataPolicy) {
|
||||
assertThat(actualRegion).isNotNull();
|
||||
assertThat(actualRegion.getName()).isEqualTo(expectedName);
|
||||
assertThat(actualRegion.getFullPath())
|
||||
.isEqualTo(GemFireUtils.toRegionPath(expectedName));
|
||||
assertThat(actualRegion.getAttributes()).isNotNull();
|
||||
assertThat(actualRegion.getAttributes().getDataPolicy())
|
||||
.isEqualTo(expectedDataPolicy);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertIndex(Index index, String expectedExpression,
|
||||
String expectedFromClause) {
|
||||
assertThat(index).isNotNull();
|
||||
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
|
||||
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertEntryIdleTimeout(Region<?, ?> region,
|
||||
ExpirationAction expectedAction, int expectedTimeout) {
|
||||
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(),
|
||||
expectedAction, expectedTimeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void assertEntryIdleTimeout(ExpirationAttributes actualExpirationAttributes,
|
||||
ExpirationAction expectedAction, int expectedTimeout) {
|
||||
assertThat(actualExpirationAttributes).isNotNull();
|
||||
assertThat(actualExpirationAttributes.getAction()).isEqualTo(expectedAction);
|
||||
assertThat(actualExpirationAttributes.getTimeout()).isEqualTo(expectedTimeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean enableQueryDebugging() {
|
||||
return DEFAULT_ENABLE_QUERY_DEBUGGING;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isQueryDebuggingEnabled() {
|
||||
return (GEMFIRE_QUERY_DEBUG || enableQueryDebugging());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected List<String> listRegions(GemFireCache gemfireCache) {
|
||||
Set<Region<?, ?>> regions = gemfireCache.rootRegions();
|
||||
|
||||
List<String> regionList = new ArrayList<String>(regions.size());
|
||||
|
||||
for (Region<?, ?> region : regions) {
|
||||
regionList.add(region.getFullPath());
|
||||
}
|
||||
|
||||
return regionList;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends ExpiringSession> T createSession() {
|
||||
T expiringSession = (T) this.sessionRepository.createSession();
|
||||
assertThat(expiringSession).isNotNull();
|
||||
return expiringSession;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends ExpiringSession> T createSession(String principalName) {
|
||||
GemFireOperationsSessionRepository.GemFireSession session = createSession();
|
||||
session.setPrincipalName(principalName);
|
||||
return (T) session;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected <T extends ExpiringSession> T expire(T session) {
|
||||
session.setLastAccessedTime(0L);
|
||||
return session;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends ExpiringSession> T get(String sessionId) {
|
||||
return (T) this.sessionRepository.getSession(sessionId);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected <T extends ExpiringSession> T save(T session) {
|
||||
this.sessionRepository.save(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected <T extends ExpiringSession> T touch(T session) {
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SessionEventListener class is a Spring {@link ApplicationListener} listening
|
||||
* for Spring HTTP Session application events.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.session.events.AbstractSessionEvent
|
||||
*/
|
||||
public static class SessionEventListener
|
||||
implements ApplicationListener<AbstractSessionEvent> {
|
||||
|
||||
private volatile AbstractSessionEvent sessionEvent;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends AbstractSessionEvent> T getSessionEvent() {
|
||||
T sessionEvent = (T) this.sessionEvent;
|
||||
this.sessionEvent = null;
|
||||
return sessionEvent;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void onApplicationEvent(AbstractSessionEvent event) {
|
||||
this.sessionEvent = event;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public <T extends AbstractSessionEvent> T waitForSessionEvent(long duration) {
|
||||
waitOnCondition(new Condition() {
|
||||
public boolean evaluate() {
|
||||
return (SessionEventListener.this.sessionEvent != null);
|
||||
}
|
||||
}, duration);
|
||||
|
||||
return getSessionEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Condition interface defines a logical condition that must be satisfied before
|
||||
* it is safe to proceed.
|
||||
*/
|
||||
protected interface Condition {
|
||||
boolean evaluate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import docs.AbstractGemFireIntegrationTests;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class HttpSessionGemFireIndexingITests extends AbstractGemFireIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void findByIndexName() {
|
||||
ExpiringSession session = sessionRepository.createSession();
|
||||
String username = "HttpSessionGemFireIndexingITests-findByIndexName-username";
|
||||
|
||||
// tag::findbyindexname-set[]
|
||||
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
|
||||
session.setAttribute(indexName, username);
|
||||
// end::findbyindexname-set[]
|
||||
|
||||
sessionRepository.save(session);
|
||||
|
||||
// tag::findbyindexname-get[]
|
||||
Map<String, ExpiringSession> idToSessions = sessionRepository
|
||||
.findByIndexNameAndIndexValue(indexName, username);
|
||||
// end::findbyindexname-get[]
|
||||
|
||||
assertThat(idToSessions.keySet()).containsOnly(session.getId());
|
||||
|
||||
sessionRepository.delete(session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser("HttpSessionGemFireIndexingITests-findBySpringSecurityIndexName")
|
||||
public void findBySpringSecurityIndexName() {
|
||||
ExpiringSession session = sessionRepository.createSession();
|
||||
|
||||
// tag::findbyspringsecurityindexname-context[]
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
Authentication authentication = context.getAuthentication();
|
||||
// end::findbyspringsecurityindexname-context[]
|
||||
|
||||
session.setAttribute(
|
||||
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
|
||||
context);
|
||||
sessionRepository.save(session);
|
||||
|
||||
// tag::findbyspringsecurityindexname-get[]
|
||||
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
|
||||
Map<String, ExpiringSession> idToSessions = sessionRepository
|
||||
.findByIndexNameAndIndexValue(indexName, authentication.getName());
|
||||
// end::findbyspringsecurityindexname-get[]
|
||||
|
||||
assertThat(idToSessions.keySet()).containsOnly(session.getId());
|
||||
|
||||
sessionRepository.delete(session.getId());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGemFireHttpSession
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", Config.class.getName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
|
||||
gemfireCache.setClose(true);
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http.gemfire.indexablesessionattributes;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import docs.AbstractGemFireIntegrationTests;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
// tag::class-start[]
|
||||
@EnableGemFireHttpSession(indexableSessionAttributes = { "name1", "name2", "name3" })
|
||||
public class GemFireHttpSessionConfig {
|
||||
// end::class-start[]
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", GemFireHttpSessionConfig.class.getName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level",
|
||||
AbstractGemFireIntegrationTests.GEMFIRE_LOG_LEVEL);
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
|
||||
gemfireCache.setClose(true);
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http.gemfire.indexablesessionattributes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import docs.AbstractGemFireIntegrationTests;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = GemFireHttpSessionConfig.class)
|
||||
public class HttpSessionGemFireIndexingCustomITests
|
||||
extends AbstractGemFireIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void findByIndexName() {
|
||||
ExpiringSession session = sessionRepository.createSession();
|
||||
String attrValue = "HttpSessionGemFireIndexingCustomITests-findByIndexName";
|
||||
|
||||
// tag::findbyindexname-set[]
|
||||
String indexName = "name1";
|
||||
session.setAttribute(indexName, attrValue);
|
||||
// end::findbyindexname-set[]
|
||||
|
||||
sessionRepository.save(session);
|
||||
|
||||
// tag::findbyindexname-get[]
|
||||
Map<String, ExpiringSession> idToSessions = sessionRepository
|
||||
.findByIndexNameAndIndexValue(indexName, attrValue);
|
||||
// end::findbyindexname-get[]
|
||||
|
||||
assertThat(idToSessions.keySet()).containsOnly(session.getId());
|
||||
|
||||
sessionRepository.delete(session.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FindByIndexNameSessionRepositoryTests {
|
||||
@Mock
|
||||
FindByIndexNameSessionRepository<Session> sessionRepository;
|
||||
@Mock
|
||||
Session session;
|
||||
|
||||
@Test
|
||||
public void setUsername() {
|
||||
// tag::set-username[]
|
||||
String username = "username";
|
||||
this.session.setAttribute(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
|
||||
// end::set-username[]
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void findByUsername() {
|
||||
// tag::findby-username[]
|
||||
String username = "username";
|
||||
Map<String, Session> sessionIdToSession = this.sessionRepository
|
||||
.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
username);
|
||||
// end::findby-username[]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
|
||||
@Autowired
|
||||
SessionRepositoryFilter<? extends ExpiringSession> filter;
|
||||
|
||||
@Test
|
||||
public void redisConnectionFactoryNotUsedSinceNoValidation() {
|
||||
assertThat(this.filter).isNotNull();
|
||||
}
|
||||
|
||||
static RedisConnectionFactory connectionFactory() {
|
||||
return mock(RedisConnectionFactory.class);
|
||||
}
|
||||
}
|
||||
157
docs/src/test/java/docs/IndexDocTests.java
Normal file
157
docs/src/test/java/docs/IndexDocTests.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class IndexDocTests {
|
||||
static final String ATTR_USER = "user";
|
||||
|
||||
@Test
|
||||
public void repositoryDemo() {
|
||||
RepositoryDemo<ExpiringSession> demo = new RepositoryDemo<ExpiringSession>();
|
||||
demo.repository = new MapSessionRepository();
|
||||
|
||||
demo.demo();
|
||||
}
|
||||
|
||||
// tag::repository-demo[]
|
||||
public class RepositoryDemo<S extends Session> {
|
||||
private SessionRepository<S> repository; // <1>
|
||||
|
||||
public void demo() {
|
||||
S toSave = this.repository.createSession(); // <2>
|
||||
|
||||
// <3>
|
||||
User rwinch = new User("rwinch");
|
||||
toSave.setAttribute(ATTR_USER, rwinch);
|
||||
|
||||
this.repository.save(toSave); // <4>
|
||||
|
||||
S session = this.repository.getSession(toSave.getId()); // <5>
|
||||
|
||||
// <6>
|
||||
User user = session.getAttribute(ATTR_USER);
|
||||
assertThat(user).isEqualTo(rwinch);
|
||||
}
|
||||
|
||||
// ... setter methods ...
|
||||
}
|
||||
// end::repository-demo[]
|
||||
|
||||
@Test
|
||||
public void expireRepositoryDemo() {
|
||||
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
|
||||
demo.repository = new MapSessionRepository();
|
||||
|
||||
demo.demo();
|
||||
}
|
||||
|
||||
// tag::expire-repository-demo[]
|
||||
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
|
||||
private SessionRepository<S> repository; // <1>
|
||||
|
||||
public void demo() {
|
||||
S toSave = this.repository.createSession(); // <2>
|
||||
// ...
|
||||
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
|
||||
|
||||
this.repository.save(toSave); // <4>
|
||||
|
||||
S session = this.repository.getSession(toSave.getId()); // <5>
|
||||
// ...
|
||||
}
|
||||
|
||||
// ... setter methods ...
|
||||
}
|
||||
// end::expire-repository-demo[]
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void newRedisOperationsSessionRepository() {
|
||||
// tag::new-redisoperationssessionrepository[]
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
SessionRepository<? extends ExpiringSession> repository = new RedisOperationsSessionRepository(
|
||||
factory);
|
||||
// end::new-redisoperationssessionrepository[]
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void mapRepository() {
|
||||
// tag::new-mapsessionrepository[]
|
||||
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
|
||||
// end::new-mapsessionrepository[]
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void newJdbcOperationsSessionRepository() {
|
||||
// tag::new-jdbcoperationssessionrepository[]
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate();
|
||||
|
||||
// ... configure JdbcTemplate ...
|
||||
|
||||
PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
|
||||
|
||||
// ... configure transactionManager ...
|
||||
|
||||
SessionRepository<? extends ExpiringSession> repository =
|
||||
new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
|
||||
// end::new-jdbcoperationssessionrepository[]
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runSpringHttpSessionConfig() {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(SpringHttpSessionConfig.class);
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.refresh();
|
||||
|
||||
try {
|
||||
context.getBean(SessionRepositoryFilter.class);
|
||||
}
|
||||
finally {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class User {
|
||||
private User(String username) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
|
||||
|
||||
@Test
|
||||
public void redisConnectionFactoryNotUsedSinceNoValidation() {
|
||||
}
|
||||
|
||||
@EnableRedisHttpSession
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
// tag::configure-redis-action[]
|
||||
@Bean
|
||||
public static ConfigureRedisAction configureRedisAction() {
|
||||
return ConfigureRedisAction.NO_OP;
|
||||
}
|
||||
// end::configure-redis-action[]
|
||||
|
||||
@Bean
|
||||
public RedisConnectionFactory redisConnectionFactory() {
|
||||
return mock(RedisConnectionFactory.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
docs/src/test/java/docs/SpringHttpSessionConfig.java
Normal file
33
docs/src/test/java/docs/SpringHttpSessionConfig.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
|
||||
// tag::class[]
|
||||
@EnableSpringHttpSession
|
||||
@Configuration
|
||||
public class SpringHttpSessionConfig {
|
||||
@Bean
|
||||
public MapSessionRepository sessionRepository() {
|
||||
return new MapSessionRepository();
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 1.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
public abstract class AbstractHttpSessionListenerTests {
|
||||
@Autowired
|
||||
ApplicationEventPublisher publisher;
|
||||
|
||||
@Autowired
|
||||
SecuritySessionDestroyedListener listener;
|
||||
|
||||
@Test
|
||||
public void springSessionDestroyedTranslatedToSpringSecurityDestroyed() {
|
||||
Session session = new MapSession();
|
||||
|
||||
this.publisher.publishEvent(
|
||||
new org.springframework.session.events.SessionDestroyedEvent(this,
|
||||
session));
|
||||
|
||||
assertThat(this.listener.getEvent().getId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
static RedisConnectionFactory createMockRedisConnection() {
|
||||
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
|
||||
RedisConnection connection = mock(RedisConnection.class);
|
||||
|
||||
given(factory.getConnection()).willReturn(connection);
|
||||
return factory;
|
||||
}
|
||||
|
||||
static class SecuritySessionDestroyedListener
|
||||
implements ApplicationListener<SessionDestroyedEvent> {
|
||||
|
||||
private SessionDestroyedEvent event;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.
|
||||
* springframework.context.ApplicationEvent)
|
||||
*/
|
||||
public void onApplicationEvent(SessionDestroyedEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public SessionDestroyedEvent getEvent() {
|
||||
return this.event;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java
Normal file
37
docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
|
||||
|
||||
//tag::config[]
|
||||
@EnableHazelcastHttpSession // <1>
|
||||
@Configuration
|
||||
public class HazelcastHttpSessionConfig {
|
||||
@Bean
|
||||
public HazelcastInstance embeddedHazelcast() {
|
||||
Config hazelcastConfig = new Config();
|
||||
return Hazelcast.newHazelcastInstance(hazelcastConfig); // <2>
|
||||
}
|
||||
}
|
||||
// end::config[]
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes = { HttpSessionListenerJavaConfigTests.MockConfig.class,
|
||||
RedisHttpSessionConfig.class })
|
||||
public class HttpSessionListenerJavaConfigTests extends AbstractHttpSessionListenerTests {
|
||||
|
||||
@Configuration
|
||||
static class MockConfig {
|
||||
|
||||
@Bean
|
||||
public static RedisConnectionFactory redisConnectionFactory() {
|
||||
return AbstractHttpSessionListenerTests.createMockRedisConnection();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecuritySessionDestroyedListener securitySessionDestroyedListener() {
|
||||
return new SecuritySessionDestroyedListener();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class HttpSessionListenerXmlTests extends AbstractHttpSessionListenerTests {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JacksonMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jakub Kubrynski
|
||||
* @author Rob Winch
|
||||
*/
|
||||
// tag::config[]
|
||||
@Configuration
|
||||
@EnableMongoHttpSession
|
||||
public class MongoJacksonSessionConfiguration {
|
||||
|
||||
@Bean
|
||||
public AbstractMongoSessionConverter mongoSessionConverter() {
|
||||
return new JacksonMongoSessionConverter(getJacksonModules());
|
||||
}
|
||||
|
||||
public Iterable<Module> getJacksonModules() {
|
||||
return Collections.<Module>singletonList(new MyJacksonModule());
|
||||
}
|
||||
}
|
||||
// end::config[]
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jakub Kubrynski
|
||||
* @author Rob Winch
|
||||
*/
|
||||
// tag::config[]
|
||||
@Configuration
|
||||
@EnableMongoHttpSession
|
||||
public class MongoJdkSessionConfiguration {
|
||||
|
||||
@Bean
|
||||
public AbstractMongoSessionConverter mongoSessionConverter() {
|
||||
return new JdkMongoSessionConverter();
|
||||
}
|
||||
}
|
||||
// end::config[]
|
||||
22
docs/src/test/java/docs/http/MyJacksonModule.java
Normal file
22
docs/src/test/java/docs/http/MyJacksonModule.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
class MyJacksonModule extends SimpleModule {
|
||||
}
|
||||
36
docs/src/test/java/docs/http/RedisHttpSessionConfig.java
Normal file
36
docs/src/test/java/docs/http/RedisHttpSessionConfig.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.http;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
|
||||
// tag::config[]
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
public class RedisHttpSessionConfig {
|
||||
|
||||
@Bean
|
||||
public HttpSessionEventPublisher httpSessionEventPublisher() {
|
||||
return new HttpSessionEventPublisher();
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
// end::config[]
|
||||
45
docs/src/test/java/docs/websocket/WebSocketConfig.java
Normal file
45
docs/src/test/java/docs/websocket/WebSocketConfig.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 docs.websocket;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
// tag::class[]
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/messages").withSockJS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.enableSimpleBroker("/queue/", "/topic/");
|
||||
registry.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.1.xsd">
|
||||
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
|
||||
|
||||
<!-- tag::configure-redis-action[] -->
|
||||
<util:constant
|
||||
static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
|
||||
<!-- end::configure-redis-action[] -->
|
||||
|
||||
<bean class="docs.HttpSessionConfigurationNoOpConfigureRedisActionXmlTests"
|
||||
factory-method="connectionFactory"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.1.xsd">
|
||||
|
||||
<!-- tag::config[] -->
|
||||
<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
|
||||
<!-- end::config[] -->
|
||||
|
||||
|
||||
<context:annotation-config/>
|
||||
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
|
||||
|
||||
<bean class="docs.http.AbstractHttpSessionListenerTests"
|
||||
factory-method="createMockRedisConnection"/>
|
||||
<bean class="docs.http.AbstractHttpSessionListenerTests$SecuritySessionDestroyedListener"/>
|
||||
</beans>
|
||||
11
eclipse/.checkstyle
Normal file
11
eclipse/.checkstyle
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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>
|
||||
295
eclipse/eclipse-code-formatter.xml
Normal file
295
eclipse/eclipse-code-formatter.xml
Normal file
@@ -0,0 +1,295 @@
|
||||
<?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>
|
||||
391
eclipse/org.eclipse.jdt.core.prefs
Normal file
391
eclipse/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,391 @@
|
||||
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
|
||||
125
eclipse/org.eclipse.jdt.ui.prefs
Normal file
125
eclipse/org.eclipse.jdt.ui.prefs
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1,27 @@
|
||||
version=1.0.0.M1
|
||||
bootstrapVersion=2.3.2
|
||||
commonsPoolVersion=2.4.2
|
||||
jacksonVersion=2.6.5
|
||||
jspApiVersion=2.0
|
||||
servletApiVersion=3.0.1
|
||||
jstlelVersion=1.2.5
|
||||
version=1.2.3.BUILD-SNAPSHOT
|
||||
springDataRedisVersion=1.7.1.RELEASE
|
||||
html5ShivVersion=3.7.3
|
||||
commonsLoggingVersion=1.2
|
||||
junitVersion=4.12
|
||||
gebVersion=0.13.1
|
||||
mockitoVersion=1.10.19
|
||||
hazelcastVersion=3.5.4
|
||||
seleniumVersion=2.52.0
|
||||
springSecurityVersion=4.0.3.RELEASE
|
||||
springVersion=4.2.5.RELEASE
|
||||
httpClientVersion=4.5.1
|
||||
jedisVersion=2.8.1
|
||||
h2Version=1.4.191
|
||||
springDataMongoVersion=1.9.1.RELEASE
|
||||
springShellVersion=1.1.0.RELEASE
|
||||
springDataGemFireVersion=1.8.1.RELEASE
|
||||
assertjVersion=2.3.0
|
||||
spockVersion=1.0-groovy-2.4
|
||||
jstlVersion=1.2.1
|
||||
groovyVersion=2.4.4
|
||||
|
||||
50
gradle/ide.gradle
Normal file
50
gradle/ide.gradle
Normal file
@@ -0,0 +1,50 @@
|
||||
import org.gradle.plugins.ide.eclipse.model.ProjectDependency
|
||||
import org.gradle.plugins.ide.eclipse.model.SourceFolder
|
||||
|
||||
|
||||
apply plugin: "propdeps-eclipse"
|
||||
apply plugin: "propdeps-idea"
|
||||
|
||||
eclipse.project.buildCommand "net.sf.eclipsecs.core.CheckstyleBuilder"
|
||||
eclipse.project.natures "net.sf.eclipsecs.core.CheckstyleNature"
|
||||
|
||||
// Include project specific settings
|
||||
task eclipseCheckstyle(type: Copy) {
|
||||
from rootProject.files(
|
||||
"eclipse/.checkstyle")
|
||||
into project.projectDir
|
||||
expand(configDir: rootProject.file('config/checkstyle').absolutePath)
|
||||
}
|
||||
|
||||
task eclipseSettings(type: Copy) {
|
||||
from rootProject.files(
|
||||
"eclipse/org.eclipse.jdt.ui.prefs",
|
||||
"eclipse/org.eclipse.wst.common.project.facet.core.xml")
|
||||
into project.file('.settings/')
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task eclipseWstComponent(type: Copy) {
|
||||
from rootProject.files(
|
||||
"eclipse/org.eclipse.wst.common.component")
|
||||
into project.file('.settings/')
|
||||
expand(deployname: project.name)
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task eclipseJdtPrepare(type: Copy) {
|
||||
from rootProject.file("eclipse/org.eclipse.jdt.core.prefs")
|
||||
into project.file(".settings/")
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
task cleanEclipseJdtUi(type: Delete) {
|
||||
delete project.file(".settings/org.eclipse.jdt.core.prefs")
|
||||
delete project.file(".settings/org.eclipse.jdt.ui.prefs")
|
||||
delete project.file(".settings/org.eclipse.wst.common.component")
|
||||
delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml")
|
||||
}
|
||||
|
||||
tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare)
|
||||
tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi)
|
||||
tasks["eclipse"].dependsOn(eclipseCheckstyle, eclipseSettings, eclipseWstComponent)
|
||||
@@ -5,72 +5,94 @@ apply plugin: 'eclipse-wtp'
|
||||
apply plugin: 'propdeps'
|
||||
apply plugin: 'propdeps-idea'
|
||||
apply plugin: 'propdeps-eclipse'
|
||||
apply plugin: 'com.github.ben-manes.versions'
|
||||
apply plugin: 'checkstyle'
|
||||
apply from: IDE_GRADLE
|
||||
|
||||
group = 'org.springframework.session'
|
||||
|
||||
ext.jstlVersion = '1.2.1'
|
||||
ext.servletApiVersion = '3.0.1'
|
||||
ext.springSecurityVersion = '3.2.4.RELEASE'
|
||||
ext.springVersion = '4.0.2.RELEASE'
|
||||
ext.groovyVersion = '2.0.5'
|
||||
ext.seleniumVersion = '2.33.0'
|
||||
ext.spockVersion = '0.7-groovy-2.0'
|
||||
ext.gebVersion = '0.9.0'
|
||||
sourceCompatibility = 1.5
|
||||
targetCompatibility = 1.5
|
||||
|
||||
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'latest.integration'
|
||||
|
||||
ext.spockDependencies = [
|
||||
dependencies.create("org.spockframework:spock-core:$spockVersion") {
|
||||
exclude group: 'junit', module: 'junit-dep'
|
||||
}
|
||||
dependencies.create("org.spockframework:spock-core:$spockVersion") {
|
||||
exclude group: 'junit', module: 'junit-dep'
|
||||
}
|
||||
]
|
||||
|
||||
ext.gebDependencies = spockDependencies + [
|
||||
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
|
||||
"org.gebish:geb-spock:$gebVersion",
|
||||
'commons-httpclient:commons-httpclient:3.1',
|
||||
"org.codehaus.groovy:groovy:$groovyVersion"
|
||||
"org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion",
|
||||
"org.gebish:geb-spock:$gebVersion",
|
||||
"org.codehaus.groovy:groovy:$groovyVersion"
|
||||
]
|
||||
|
||||
ext.jstlDependencies = [
|
||||
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:$jstlVersion",
|
||||
"org.apache.taglibs:taglibs-standard-jstlel:1.2.1"
|
||||
"javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:$jstlVersion",
|
||||
"org.apache.taglibs:taglibs-standard-jstlel:1.2.1"
|
||||
]
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'http://clojars.org/repo' }
|
||||
mavenCentral()
|
||||
maven { url 'https://repo.spring.io/libs-snapshot' }
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == 'org.springframework') {
|
||||
details.useVersion springVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
// Integration test setup
|
||||
configurations {
|
||||
integrationTestCompile {
|
||||
extendsFrom testCompile, optional, provided
|
||||
}
|
||||
integrationTestRuntime {
|
||||
extendsFrom integrationTestCompile, testRuntime
|
||||
}
|
||||
integrationTestCompile {
|
||||
extendsFrom testCompile, optional, provided
|
||||
}
|
||||
integrationTestRuntime {
|
||||
extendsFrom integrationTestCompile, testRuntime
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
integrationTest {
|
||||
java.srcDir file('src/integration-test/java')
|
||||
groovy.srcDirs file('src/integration-test/groovy')
|
||||
resources.srcDir file('src/integration-test/resources')
|
||||
compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile
|
||||
runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime
|
||||
}
|
||||
integrationTest {
|
||||
java.srcDir file('src/integration-test/java')
|
||||
groovy.srcDirs file('src/integration-test/groovy')
|
||||
resources.srcDir file('src/integration-test/resources')
|
||||
compileClasspath = sourceSets.main.output + sourceSets.test.output + configurations.integrationTestCompile
|
||||
runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime
|
||||
}
|
||||
}
|
||||
|
||||
task integrationTest(type: Test, dependsOn: jar) {
|
||||
testClassesDir = sourceSets.integrationTest.output.classesDir
|
||||
logging.captureStandardOutput(LogLevel.INFO)
|
||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||
maxParallelForks = 1
|
||||
reports {
|
||||
html.destination = project.file("$project.buildDir/reports/integration-tests/")
|
||||
junitXml.destination = project.file("$project.buildDir/integration-test-results/")
|
||||
}
|
||||
testClassesDir = sourceSets.integrationTest.output.classesDir
|
||||
logging.captureStandardOutput(LogLevel.INFO)
|
||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||
maxParallelForks = 1
|
||||
reports {
|
||||
html.destination = project.file("$project.buildDir/reports/integration-tests/")
|
||||
junitXml.destination = project.file("$project.buildDir/integration-test-results/")
|
||||
}
|
||||
}
|
||||
check.dependsOn integrationTest
|
||||
|
||||
checkstyle {
|
||||
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
|
||||
configProperties.configDir = configFile.parentFile
|
||||
toolVersion = '6.16.1'
|
||||
}
|
||||
|
||||
task checkstyle {
|
||||
dependsOn project.tasks.findAll { task -> task.name.matches('checkstyle\\w+') }
|
||||
}
|
||||
|
||||
eclipse {
|
||||
classpath {
|
||||
plusConfigurations += [ configurations.integrationTestCompile ]
|
||||
}
|
||||
}
|
||||
|
||||
project.idea.module {
|
||||
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
|
||||
}
|
||||
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
|
||||
}
|
||||
|
||||
51
gradle/publish-maven.gradle
Normal file
51
gradle/publish-maven.gradle
Normal file
@@ -0,0 +1,51 @@
|
||||
apply plugin: 'propdeps-maven'
|
||||
|
||||
install {
|
||||
repositories.mavenInstaller {
|
||||
customizePom(pom, project)
|
||||
}
|
||||
}
|
||||
|
||||
def customizePom(pom, gradleProject) {
|
||||
pom.whenConfigured { generatedPom ->
|
||||
|
||||
// sort to make pom dependencies order consistent to ease comparison of older poms
|
||||
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
|
||||
"$dep.scope:$dep.groupId:$dep.artifactId"
|
||||
}
|
||||
|
||||
// add all items necessary for maven central publication
|
||||
generatedPom.project {
|
||||
name = gradleProject.description
|
||||
description = gradleProject.description
|
||||
url = "https://github.com/spring-projects/spring-session"
|
||||
organization {
|
||||
name = "Spring IO"
|
||||
url = "https://projects.spring.io/spring-session"
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name "The Apache Software License, Version 2.0"
|
||||
url "https://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
distribution "repo"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url = "https://github.com/spring-projects/spring-session"
|
||||
connection = "scm:git:git://github.com/spring-projects/spring-session"
|
||||
developerConnection = "scm:git:git://github.com/spring-projects/spring-session"
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = "rwinch"
|
||||
name = "Rob Winch"
|
||||
email = "rwinch@pivotal.io"
|
||||
}
|
||||
}
|
||||
issueManagement {
|
||||
system = "GitHub"
|
||||
url = "https://github.com/spring-projects/spring-session/issues"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
gradle/sample.gradle
Normal file
4
gradle/sample.gradle
Normal file
@@ -0,0 +1,4 @@
|
||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||
sonarqube {
|
||||
skipProject = true
|
||||
}
|
||||
24
gradle/spring3.gradle
Normal file
24
gradle/spring3.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
configurations {
|
||||
spring3TestRuntime.extendsFrom testRuntime
|
||||
}
|
||||
|
||||
configurations.spring3TestRuntime {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == 'org.springframework'
|
||||
&& details.requested.name != 'spring-websocket'
|
||||
&& details.requested.name != 'spring-messaging') {
|
||||
details.useVersion '3.2.14.RELEASE'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task spring3Test(type: Test) {
|
||||
jvmArgs = ['-ea', '-Xmx500m', '-XX:MaxPermSize=128M']
|
||||
classpath = sourceSets.test.output + sourceSets.main.output + configurations.spring3TestRuntime
|
||||
exclude "org/springframework/session/web/socket/**"
|
||||
reports {
|
||||
html.destination = project.file("$buildDir/spring3-test-results/")
|
||||
junitXml.destination = project.file("$buildDir/reports/spring3-tests/")
|
||||
}
|
||||
}
|
||||
check.dependsOn spring3Test
|
||||
@@ -1,71 +1,64 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
}
|
||||
repositories {
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'war'
|
||||
apply plugin: 'tomcat'
|
||||
|
||||
dependencies {
|
||||
def tomcatVersion = '7.0.54'
|
||||
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
|
||||
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
|
||||
tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
|
||||
exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
|
||||
}
|
||||
}
|
||||
|
||||
[tomcatRun,tomcatRunWar]*.contextPath = '/'
|
||||
|
||||
|
||||
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
buildscriptClasspath = tomcatRun.buildscriptClasspath
|
||||
contextPath = tomcatRun.contextPath
|
||||
daemon = true
|
||||
tomcatClasspath = tomcatRun.tomcatClasspath
|
||||
webAppClasspath = tomcatRun.webAppClasspath
|
||||
webAppSourceDirectory = tomcatRun.webAppSourceDirectory
|
||||
doFirst {
|
||||
def mainOutputDir = project.sourceSets.main.output.classesDir
|
||||
if(mainOutputDir) {
|
||||
classesDirectory = mainOutputDir
|
||||
}
|
||||
// delay reserving ports to ensure they are still available
|
||||
def ports = reservePorts(3)
|
||||
httpPort = ports[0]
|
||||
ajpPort = ports[1]
|
||||
stopPort = ports[2]
|
||||
}
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
buildscriptClasspath = tomcatRun.buildscriptClasspath
|
||||
contextPath = tomcatRun.contextPath
|
||||
daemon = true
|
||||
tomcatClasspath = tomcatRun.tomcatClasspath
|
||||
webAppClasspath = tomcatRun.webAppClasspath
|
||||
webAppSourceDirectory = tomcatRun.webAppSourceDirectory
|
||||
doFirst {
|
||||
def mainOutputDir = project.sourceSets.main.output.classesDir
|
||||
if(mainOutputDir) {
|
||||
classesDirectory = mainOutputDir
|
||||
}
|
||||
// delay reserving ports to ensure they are still available
|
||||
def ports = reservePorts(3)
|
||||
httpPort = ports[0]
|
||||
ajpPort = ports[1]
|
||||
stopPort = ports[2]
|
||||
|
||||
System.setProperty('spring.session.redis.namespace',project.name)
|
||||
}
|
||||
}
|
||||
|
||||
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
doFirst {
|
||||
stopPort = integrationTomcatRun.stopPort
|
||||
}
|
||||
onlyIf { !sourceSets.integrationTest.allSource.empty }
|
||||
doFirst {
|
||||
stopPort = integrationTomcatRun.stopPort
|
||||
}
|
||||
}
|
||||
|
||||
integrationTest {
|
||||
dependsOn integrationTomcatRun
|
||||
doFirst {
|
||||
def host = 'localhost:' + integrationTomcatRun.httpPort
|
||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + integrationTomcatRun.contextPath
|
||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||
}
|
||||
finalizedBy integrationTomcatStop
|
||||
dependsOn integrationTomcatRun
|
||||
doFirst {
|
||||
def host = 'localhost:' + integrationTomcatRun.httpPort
|
||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/' + integrationTomcatRun.contextPath
|
||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||
}
|
||||
finalizedBy integrationTomcatStop
|
||||
}
|
||||
|
||||
def reservePorts(int count) {
|
||||
def sockets = []
|
||||
for(int i in 1..count) {
|
||||
sockets << new ServerSocket(0)
|
||||
}
|
||||
def result = sockets*.localPort
|
||||
sockets*.close()
|
||||
result
|
||||
def sockets = []
|
||||
for(int i in 1..count) {
|
||||
sockets << new ServerSocket(0)
|
||||
}
|
||||
def result = sockets*.localPort
|
||||
sockets*.close()
|
||||
result
|
||||
}
|
||||
8
gradle/tomcat6.gradle
Normal file
8
gradle/tomcat6.gradle
Normal file
@@ -0,0 +1,8 @@
|
||||
apply from: TOMCAT_GRADLE
|
||||
|
||||
dependencies {
|
||||
def tomcatVersion = '6.0.43'
|
||||
tomcat "org.apache.tomcat:catalina:${tomcatVersion}",
|
||||
"org.apache.tomcat:coyote:${tomcatVersion}",
|
||||
"org.apache.tomcat:jasper:${tomcatVersion}"
|
||||
}
|
||||
10
gradle/tomcat7.gradle
Normal file
10
gradle/tomcat7.gradle
Normal file
@@ -0,0 +1,10 @@
|
||||
apply from: TOMCAT_GRADLE
|
||||
|
||||
dependencies {
|
||||
def tomcatVersion = '7.0.59'
|
||||
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
|
||||
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
|
||||
tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
|
||||
exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
|
||||
}
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Wed Jun 18 14:02:09 CDT 2014
|
||||
#Tue Nov 25 20:57:10 CST 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
|
||||
|
||||
2
gradlew
vendored
2
gradlew
vendored
@@ -7,7 +7,7 @@
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS="-Xmx1024M -XX:MaxPermSize=512M"
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -9,7 +9,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS=-Xmx1024M -XX:MaxPermSize=512M
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
|
||||
1
samples/boot/README.adoc
Normal file
1
samples/boot/README.adoc
Normal file
@@ -0,0 +1 @@
|
||||
Demonstrates using Spring Session with Spring Boot and Spring Security. You can log in with the username "user" and the password "password".
|
||||
55
samples/boot/build.gradle
Normal file
55
samples/boot/build.gradle
Normal file
@@ -0,0 +1,55 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
group = 'samples'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session'),
|
||||
"org.springframework.boot:spring-boot-starter-redis",
|
||||
"org.springframework.boot:spring-boot-starter-web",
|
||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
|
||||
integrationTestCompile gebDependencies,
|
||||
"org.spockframework:spock-spring:$spockVersion"
|
||||
|
||||
}
|
||||
|
||||
integrationTest {
|
||||
doFirst {
|
||||
def port = reservePort()
|
||||
|
||||
def host = 'localhost:' + port
|
||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
|
||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||
systemProperties['server.port'] = port
|
||||
systemProperties['management.port'] = 0
|
||||
|
||||
systemProperties['spring.session.redis.namespace'] = project.name
|
||||
}
|
||||
}
|
||||
|
||||
def reservePort() {
|
||||
def socket = new ServerSocket(0)
|
||||
def result = socket.localPort
|
||||
socket.close()
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample
|
||||
|
||||
import geb.spock.*
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.test.IntegrationTest
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
import sample.pages.HomePage
|
||||
import sample.pages.LoginPage
|
||||
import spock.lang.Stepwise
|
||||
import pages.*
|
||||
|
||||
/**
|
||||
* Tests the demo that supports multiple sessions
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Stepwise
|
||||
@ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
class BootTests extends GebReportingSpec {
|
||||
|
||||
def 'Unauthenticated user sent to log in page'() {
|
||||
when: 'unauthenticated user request protected page'
|
||||
via HomePage
|
||||
then: 'sent to the log in page'
|
||||
at LoginPage
|
||||
}
|
||||
|
||||
def 'Log in views home page'() {
|
||||
when: 'log in successfully'
|
||||
login()
|
||||
then: 'sent to original page'
|
||||
at HomePage
|
||||
and: 'the username is displayed'
|
||||
username == 'user'
|
||||
and: 'Spring Session Management is being used'
|
||||
driver.manage().cookies.find { it.name == 'SESSION' }
|
||||
and: 'Standard Session is NOT being used'
|
||||
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||
}
|
||||
|
||||
def 'Log out success'() {
|
||||
when:
|
||||
logout()
|
||||
then:
|
||||
at LoginPage
|
||||
}
|
||||
|
||||
def 'Logged out user sent to log in page'() {
|
||||
when: 'logged out user request protected page'
|
||||
via HomePage
|
||||
then: 'sent to the log in page'
|
||||
at LoginPage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
|
||||
static content = {
|
||||
username { $('#un').text() }
|
||||
logout(to:LoginPage) { $('input[type=submit]').click() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The Links Page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LoginPage extends Page {
|
||||
static url = '/login'
|
||||
static at = { assert driver.title == 'Login Page'; true}
|
||||
static content = {
|
||||
form { $('form') }
|
||||
submit { $('input[type=submit]') }
|
||||
login(required:false) { user='user', pass='password' ->
|
||||
form.username = user
|
||||
form.password = pass
|
||||
submit.click(HomePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
31
samples/boot/src/main/java/sample/Application.java
Normal file
31
samples/boot/src/main/java/sample/Application.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.config;
|
||||
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
|
||||
// tag::class[]
|
||||
@EnableRedisHttpSession // <1>
|
||||
public class HttpSessionConfig {
|
||||
}
|
||||
// end::class[]
|
||||
34
samples/boot/src/main/java/sample/config/SecurityConfig.java
Normal file
34
samples/boot/src/main/java/sample/config/SecurityConfig.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
|
||||
}
|
||||
}
|
||||
34
samples/boot/src/main/java/sample/mvc/IndexController.java
Normal file
34
samples/boot/src/main/java/sample/mvc/IndexController.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.mvc;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* Controller for sending the user to the login view.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
public class IndexController {
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
2
samples/boot/src/main/resources/application.properties
Normal file
2
samples/boot/src/main/resources/application.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
spring.thymeleaf.cache=false
|
||||
spring.template.cache=false
|
||||
BIN
samples/boot/src/main/resources/static/resources/img/favicon.ico
Normal file
BIN
samples/boot/src/main/resources/static/resources/img/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
samples/boot/src/main/resources/static/resources/img/logo.png
Normal file
BIN
samples/boot/src/main/resources/static/resources/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
11
samples/boot/src/main/resources/templates/index.html
Normal file
11
samples/boot/src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<html xmlns:th="https://www.thymeleaf.org" xmlns:layout="https://github.com/ultraq/thymeleaf-layout-dialect" layout:decorator="layout">
|
||||
<head>
|
||||
<title>Secured Content</title>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<h1>Secured Page</h1>
|
||||
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
122
samples/boot/src/main/resources/templates/layout.html
Normal file
122
samples/boot/src/main/resources/templates/layout.html
Normal file
@@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html SYSTEM "https://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
xmlns:layout="https://github.com/ultraq/thymeleaf-layout-dialect">
|
||||
<head>
|
||||
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Spring Session Sample</title>
|
||||
<link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../static/img/favicon.ico"/>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
|
||||
<style type="text/css">
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
}
|
||||
|
||||
/* Wrapper for page content to push down footer */
|
||||
#wrap {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
/* Negative indent footer by it's height */
|
||||
margin: 0 auto -60px;
|
||||
}
|
||||
|
||||
/* Set the fixed height of the footer here */
|
||||
#push,
|
||||
#footer {
|
||||
height: 60px;
|
||||
}
|
||||
#footer {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* Lastly, apply responsive CSS fixes as necessary */
|
||||
@media (max-width: 767px) {
|
||||
#footer {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Custom page CSS
|
||||
-------------------------------------------------- */
|
||||
/* Not required for template or sticky footer method. */
|
||||
|
||||
.container {
|
||||
width: auto;
|
||||
max-width: 680px;
|
||||
}
|
||||
.container .credit {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
a {
|
||||
color: green;
|
||||
}
|
||||
.navbar-form {
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
||||
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<div class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a>
|
||||
|
||||
<div class="nav-collapse collapse"
|
||||
th:with="currentUser=${#httpServletRequest.userPrincipal?.principal}">
|
||||
<div th:if="${currentUser != null}">
|
||||
<form class="navbar-form pull-right" th:action="@{/logout}" method="post">
|
||||
<input type="submit" value="Log out" />
|
||||
</form>
|
||||
<p id="un" class="navbar-text pull-right" th:text="${currentUser.username}">
|
||||
sample_user
|
||||
</p>
|
||||
</div>
|
||||
<ul class="nav">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Begin page content -->
|
||||
<div class="container">
|
||||
<div class="alert alert-success"
|
||||
th:if="${globalMessage}"
|
||||
th:text="${globalMessage}">
|
||||
Some Success message
|
||||
</div>
|
||||
<div layout:fragment="content">
|
||||
Fake content
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="push"><!-- --></div>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="muted credit">Visit the <a href="https://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
16
samples/custom-cookie/build.gradle
Normal file
16
samples/custom-cookie/build.gradle
Normal file
@@ -0,0 +1,16 @@
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: TOMCAT_7_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
testCompile "junit:junit:$junitVersion"
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample
|
||||
|
||||
import geb.spock.*
|
||||
import sample.pages.HomePage;
|
||||
import spock.lang.Stepwise
|
||||
import pages.*
|
||||
|
||||
/**
|
||||
* Tests the CAS sample application using service tickets.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Stepwise
|
||||
class AttributeTests extends GebReportingSpec {
|
||||
def 'first visit no attributes'() {
|
||||
when:
|
||||
to HomePage
|
||||
then:
|
||||
attributes.empty
|
||||
}
|
||||
|
||||
def 'create attribute'() {
|
||||
when:
|
||||
createAttribute('a','b')
|
||||
then:
|
||||
attributes.size() == 1
|
||||
attributes[0].name == 'a'
|
||||
attributes[0].value == 'b'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Session Attributes'; true}
|
||||
static content = {
|
||||
form { $('form') }
|
||||
submit { $('input[type=submit]') }
|
||||
createAttribute(required:false) { name, value ->
|
||||
form.attributeName = name
|
||||
form.attributeValue = value
|
||||
submit.click(HomePage)
|
||||
}
|
||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||
}
|
||||
}
|
||||
class AttributeRow extends Module {
|
||||
static content = {
|
||||
cell { $("td", it) }
|
||||
name { cell(0).text() }
|
||||
value { cell(1).text() }
|
||||
}
|
||||
}
|
||||
43
samples/custom-cookie/src/main/java/sample/Config.java
Normal file
43
samples/custom-cookie/src/main/java/sample/Config.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.DefaultCookieSerializer;
|
||||
|
||||
@EnableRedisHttpSession
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public JedisConnectionFactory connectionFactory() {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
// tag::cookie-serializer[]
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
|
||||
serializer.setCookieName("JSESSIONID"); // <1>
|
||||
serializer.setCookiePath("/"); // <2>
|
||||
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); // <3>
|
||||
return serializer;
|
||||
}
|
||||
// end::cookie-serializer[]
|
||||
}
|
||||
28
samples/custom-cookie/src/main/java/sample/Initializer.java
Normal file
28
samples/custom-cookie/src/main/java/sample/Initializer.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
|
||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||
|
||||
// tag::class[]
|
||||
public class Initializer extends AbstractHttpSessionApplicationInitializer { // <1>
|
||||
|
||||
public Initializer() {
|
||||
super(Config.class); // <2>
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
// tag::class[]
|
||||
@WebServlet("/session")
|
||||
public class SessionServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
String attributeName = req.getParameter("attributeName");
|
||||
String attributeValue = req.getParameter("attributeValue");
|
||||
req.getSession().setAttribute(attributeName, attributeValue);
|
||||
resp.sendRedirect(req.getContextPath() + "/");
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 2878267318695777395L;
|
||||
}
|
||||
// tag::end[]
|
||||
48
samples/custom-cookie/src/main/webapp/index.jsp
Normal file
48
samples/custom-cookie/src/main/webapp/index.jsp
Normal file
@@ -0,0 +1,48 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Session Attributes</title>
|
||||
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Description</h1>
|
||||
<p>This application demonstrates how to use a Redis instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
|
||||
|
||||
<h1>Try it</h1>
|
||||
|
||||
<form class="form-inline" role="form" action="./session" method="post">
|
||||
<label for="attributeName">Attribute Name</label>
|
||||
<input id="attributeName" type="text" name="attributeName"/>
|
||||
<label for="attributeValue">Attribute Value</label>
|
||||
<input id="attributeValue" type="text" name="attributeValue"/>
|
||||
<input type="submit" value="Set Attribute"/>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute Name</th>
|
||||
<th>Attribute Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<c:forEach items="${sessionScope}" var="attr">
|
||||
<tr>
|
||||
<td><c:out value="${attr.key}"/></td>
|
||||
<td><c:out value="${attr.value}"/></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
4
samples/findbyusername/README.adoc
Normal file
4
samples/findbyusername/README.adoc
Normal file
@@ -0,0 +1,4 @@
|
||||
Demonstrates using Spring Session to lookup a user's session by the username.
|
||||
The sample provides a hook to add the current username to the session (required for finding the user) by providing a custom implementation of Spring Security's `AuthenticationSuccessHandler`.
|
||||
|
||||
NOTE: This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com
|
||||
58
samples/findbyusername/build.gradle
Normal file
58
samples/findbyusername/build.gradle
Normal file
@@ -0,0 +1,58 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
group = 'samples'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session'),
|
||||
"org.springframework.boot:spring-boot-starter-redis",
|
||||
"org.springframework.boot:spring-boot-starter-web",
|
||||
"org.springframework.boot:spring-boot-starter-thymeleaf",
|
||||
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
|
||||
"org.thymeleaf.extras:thymeleaf-extras-conditionalcomments",
|
||||
"org.webjars:bootstrap:$bootstrapVersion",
|
||||
"org.webjars:html5shiv:$html5ShivVersion",
|
||||
"org.springframework.security:spring-security-web:$springSecurityVersion",
|
||||
"org.springframework.security:spring-security-config:$springSecurityVersion",
|
||||
"com.maxmind.geoip2:geoip2:2.3.1",
|
||||
"org.apache.httpcomponents:httpclient"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test",
|
||||
"org.assertj:assertj-core:$assertjVersion"
|
||||
|
||||
integrationTestCompile gebDependencies,
|
||||
"org.spockframework:spock-spring:$spockVersion"
|
||||
|
||||
}
|
||||
|
||||
integrationTest {
|
||||
doFirst {
|
||||
def port = reservePort()
|
||||
|
||||
def host = 'localhost:' + port
|
||||
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
|
||||
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
|
||||
systemProperties['server.port'] = port
|
||||
systemProperties['management.port'] = 0
|
||||
|
||||
systemProperties['spring.session.redis.namespace'] = project.name
|
||||
}
|
||||
}
|
||||
|
||||
def reservePort() {
|
||||
def socket = new ServerSocket(0)
|
||||
def result = socket.localPort
|
||||
socket.close()
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample
|
||||
|
||||
import geb.Browser
|
||||
import geb.spock.*
|
||||
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver
|
||||
import org.springframework.boot.test.IntegrationTest
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
|
||||
import pages.*
|
||||
import sample.pages.HomePage
|
||||
import sample.pages.LoginPage
|
||||
import spock.lang.Stepwise
|
||||
|
||||
/**
|
||||
* Tests findbyusername web application
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Stepwise
|
||||
@ContextConfiguration(classes = FindByUsernameApplication, loader = SpringApplicationContextLoader)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
class FindByUsernameTests extends MultiBrowserGebSpec {
|
||||
def 'Unauthenticated user sent to log in page'() {
|
||||
given: 'unauthenticated user request protected page'
|
||||
withBrowserSession a, {
|
||||
via HomePage
|
||||
then: 'sent to the log in page'
|
||||
at LoginPage
|
||||
}
|
||||
}
|
||||
|
||||
def 'Log in views home page'() {
|
||||
when: 'log in successfully'
|
||||
a.login()
|
||||
then: 'sent to original page'
|
||||
a.at HomePage
|
||||
and: 'the username is displayed'
|
||||
a.username == 'user'
|
||||
and: 'Spring Session Management is being used'
|
||||
a.driver.manage().cookies.find { it.name == 'SESSION' }
|
||||
and: 'Standard Session is NOT being used'
|
||||
!a.driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||
and: 'Session id exists and terminate disabled'
|
||||
a.sessionId
|
||||
a.terminate(a.sessionId).@disabled
|
||||
}
|
||||
|
||||
def 'Other Browser: Unauthenticated user sent to log in page'() {
|
||||
given:
|
||||
withBrowserSession b, {
|
||||
via HomePage
|
||||
then: 'sent to the log in page'
|
||||
at LoginPage
|
||||
}
|
||||
}
|
||||
|
||||
def 'Other Browser: Log in views home page'() {
|
||||
when: 'log in successfully'
|
||||
b.login()
|
||||
then: 'sent to original page'
|
||||
b.at HomePage
|
||||
and: 'the username is displayed'
|
||||
b.username == 'user'
|
||||
and: 'Spring Session Management is being used'
|
||||
b.driver.manage().cookies.find { it.name == 'SESSION' }
|
||||
and: 'Standard Session is NOT being used'
|
||||
!b.driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||
and: 'Session id exists and terminate disabled'
|
||||
b.sessionId
|
||||
b.terminate(b.sessionId).@disabled
|
||||
}
|
||||
|
||||
def 'Other Browser: Terminate Original'() {
|
||||
setup: 'get the session id from browser a'
|
||||
def sessionId = a.sessionId
|
||||
when: 'we terminate browser a session'
|
||||
b.terminate(sessionId).click(HomePage)
|
||||
then: 'session is not listed'
|
||||
!b.terminate(sessionId)
|
||||
when: 'browser a visits home page after session was terminated'
|
||||
a.via HomePage
|
||||
then: 'browser a sent to log in'
|
||||
a.at LoginPage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
import geb.*
|
||||
import spock.lang.*
|
||||
|
||||
/**
|
||||
* https://github.com/kensiprell/geb-multibrowser
|
||||
*/
|
||||
abstract class MultiBrowserGebSpec extends Specification {
|
||||
String gebConfEnv = null
|
||||
String gebConfScript = null
|
||||
|
||||
// Map of geb browsers which can be referenced by name in the spec
|
||||
// THese currently share the same config. This is not a problem for
|
||||
// my uses, but I can see potential for wanting to configure different
|
||||
// browsers separately
|
||||
@Shared _browsers = createBrowserMap()
|
||||
def currentBrowser
|
||||
|
||||
def createBrowserMap() {
|
||||
[:].withDefault { new Browser(createConf()) }
|
||||
}
|
||||
|
||||
Configuration createConf() {
|
||||
// Use the standard configured geb driver, but turn off cacheing so
|
||||
// we can run multiple
|
||||
def conf = new ConfigurationLoader(gebConfEnv).getConf(gebConfScript)
|
||||
conf.cacheDriver = false
|
||||
return conf
|
||||
}
|
||||
|
||||
def withBrowserSession(browser, Closure c) {
|
||||
currentBrowser = browser
|
||||
def returnedValue = c.call()
|
||||
currentBrowser = null
|
||||
returnedValue
|
||||
}
|
||||
|
||||
void resetBrowsers() {
|
||||
_browsers.each { k, browser ->
|
||||
if (browser.config?.autoClearCookies) {
|
||||
browser.clearCookiesQuietly()
|
||||
}
|
||||
browser.quit()
|
||||
}
|
||||
_browsers = createBrowserMap()
|
||||
}
|
||||
|
||||
def propertyMissing(String name) {
|
||||
if(currentBrowser) {
|
||||
return currentBrowser."$name"
|
||||
} else {
|
||||
return _browsers[name]
|
||||
}
|
||||
}
|
||||
|
||||
def methodMissing(String name, args) {
|
||||
if(currentBrowser) {
|
||||
return currentBrowser."$name"(*args)
|
||||
} else {
|
||||
def browser = _browsers[name]
|
||||
if(args) {
|
||||
return browser."${args[0]}"(*(args[1..-1]))
|
||||
} else {
|
||||
return browser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def propertyMissing(String name, value) {
|
||||
if(!currentBrowser) throw new IllegalArgumentException("No context for setting property $name")
|
||||
currentBrowser."$name" = value
|
||||
}
|
||||
|
||||
private isSpecStepwise() {
|
||||
this.class.getAnnotation(Stepwise) != null
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
if (!isSpecStepwise()) resetBrowsers()
|
||||
}
|
||||
|
||||
def cleanupSpec() {
|
||||
if (isSpecStepwise()) resetBrowsers()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Spring Session Sample - Secured Content'; true}
|
||||
static content = {
|
||||
username { $('#un').text() }
|
||||
logout(to:LoginPage) { $('input[type=submit]').click() }
|
||||
sessions { moduleList AttributeRow, $("table tr").tail() }
|
||||
terminate(required:false) { sessionId -> $("#terminate-$sessionId") }
|
||||
sessionId { $("#session-id").text() }
|
||||
}
|
||||
}
|
||||
|
||||
class AttributeRow extends Module {
|
||||
static content = {
|
||||
cell { $("td", it) }
|
||||
location { cell(0).text() }
|
||||
created { cell(1).text() }
|
||||
lastUpdated { cell(2).text() }
|
||||
information { cell(3).text() }
|
||||
terminate { cell(4).text() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The Links Page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LoginPage extends Page {
|
||||
static url = '/login'
|
||||
static at = { assert driver.title == 'Spring Session Sample - Log In'; true}
|
||||
static content = {
|
||||
form { $('form') }
|
||||
submit { $('button[type=submit]') }
|
||||
login(required:false) { user='user', pass='password' ->
|
||||
form.username = user
|
||||
form.password = pass
|
||||
submit.click(HomePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
public class FindByUsernameApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FindByUsernameApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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 sample.config;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.maxmind.geoip2.DatabaseReader;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class GeoConfig {
|
||||
|
||||
@Bean
|
||||
public DatabaseReader geoDatabaseReader(
|
||||
@Value("classpath:GeoLite2-City.mmdb") InputStream geoInputStream)
|
||||
throws Exception {
|
||||
return new DatabaseReader.Builder(geoInputStream).build();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user