Compare commits

..

142 Commits

Author SHA1 Message Date
Spring Buildmaster
245e634bea Release version 1.3.0.M2 2016-09-14 19:08:14 +00:00
Rob Winch
7a2914323f Update to Hazelcast 3.6.5
Issue gh-544
2016-09-14 11:32:03 -05:00
Vedran Pavic
6e04d903ae Add HazelcastSessionRepository
This commit improves existing Hazelcast support, which is based on
MapSessionRepository, with dedicated HazelcastSessionRepository
that implements the FindByIndexNameSessionRepository contract.

Also a new hazelcast-spring-session module was added to provide
dependency management for Hazelcast support.

Fixes gh-544
2016-09-14 11:22:56 -05:00
Spring Buildmaster
d8c3a4dd61 Next development version 2016-09-13 21:21:21 +00:00
Spring Buildmaster
997813088a Release version 1.3.0.M1 2016-09-13 21:21:14 +00:00
Rob Winch
5ecf390932 Add spring.session.cleanup.cron.expression
Fixes gh-616
2016-09-13 10:41:31 -05:00
Rob Winch
8167b43e63 Update to AssertJ 2.5.0 2016-09-13 10:29:14 -05:00
Rob Winch
1f7193f32d Polish
Issue gh-434
2016-09-12 16:02:45 -05:00
Rob Winch
0e1d81f509 Add Spring Security jackson2
Issue gh-434
2016-09-12 16:02:40 -05:00
Jitendra Singh Bisht
8b97a32db2 Mix-ins added for Jackson Serialization/deserialization
Fixes gh-434
2016-09-12 16:02:21 -05:00
Vedran Pavic
d3379029bb Fix custom expire for JdbcOperationsSessionRepository.cleanUpExpiredSessions
Currently, JdbcOperationsSessionRepository#cleanUpExpiredSessions only considers
the repository defined max inactive interval which causes incorrect cleanup of
sessions that define custom inactive interval. This commit fixes the problem by
delegating calculation of deletion interval to the underlying SQL DELETE statement.

Fixes gh-580
2016-09-12 13:09:36 -05:00
Vedran Pavic
26eca5b448 Add custom SQL JdbcOperationsSessionRepository
This commit allows custom SQL queries for
JdbcOperationsSessionRepository.

Fixes gh-609
2016-09-12 13:08:15 -05:00
Vedran Pavić
6335894e13 Restore Unix (LF) line endings in CookieHttpSessionStrategyTests (#625) 2016-09-12 12:54:12 -05:00
Vedran Pavic
34948d6451 Improve JDBC integration tests 2016-09-12 11:28:05 -05:00
Eric Deandrea
bff0f8f845 Fix CookieHttpSessionStrategy encodeUrl
Fix to https://support.pivotal.io/tickets/30469

I do notice 2 bugs in the spring-session code, both inside the
CookieHttpSessionStrategy class.

The first, using their own sample application, after getting a new
session alias from the HttpSessionManager, it calls
HttpSessionManager.encodeURL and then stores the output into a request
attribute which is later rendered. However, in a Spring MVC
application, a Controller may want to issue a redirect to a
newly-encoded URL, like this:

HttpSessionManager sessionManager = (HttpSessionManager)
request.getAttribute(HttpSessionManager.class.getName());
String newSessionAlias = sessionManager.getNewSessionAlias(request);
String currentSessionAlias =
sessionManager.getCurrentSessionAlias(request);

return String.format("redirect:%s",
sessionManager.encodeURL("/effectiveUser", newSessionAlias));

The problem here is that Spring MVC will funnel the redirect back
through
CookieHttpSessionStrategy.MultiSessionHttpServletResponse.encodeRedirect
URL, which then looks up the current alias & re-encodes with that
alias, effectively replacing the new alias which the controller added.
The fix would be for the
CookieHttpSessionStrategy.MultiSessionHttpServletResponse to examine
the inputted url for the appropriate parameter & don't re-encode if it
finds it there.

The second issue is that inside CookieHttpSessionStrategy line 346
(return path + "?" + query;) is that if there is no query string (on
the default session alias) then the resulting URL will end in a ? with
nothing after it. In that situation the ? should be removed as well
because the query string is empty.
2016-09-12 11:03:57 -05:00
Rob Winch
2052ec8d44 Polish JDBC Bean ClassLoader
Issue gh-610
2016-09-07 11:08:59 -05:00
Vedran Pavic
cbd96999e0 JDBC uses Bean ClassLoader
This commit addresses the issue with deserializing JDBC sessions in Spring Boot
applications that use DevTools. Previously, such configuration would cause
`ClassCastException` when deserializing JDBC sessions due to app class loader
being used instead of restart class loader.

Fixes gh-610
2016-09-07 11:08:58 -05:00
Rob Winch
61492c4ae1 Add ability to set delimiters for CookieHttpSessionStrategy
Fixes gh-615
2016-09-06 23:20:53 -05:00
Rob Winch
97fef0f9bd Polish Base64 DefaultCookieSerializer Support
Issue gh-611
2016-09-06 21:20:20 -05:00
Vedran Pavic
2792d2a0e9 Add DefaultCookieSerializer Base64 Support
Fixes gh-611
2016-09-06 21:19:52 -05:00
Joris Kuipers
2724b333b3 Spring security session registry (#473)
*  Spring Security Concurrent Session Integration #65

 add SpringSessionBackedSessionRegistry

*  Spring Security Concurrent Session Integration #65

 add documentation

*  Spring Security Concurrent Session Integration #65

support marking SessionInformations as expired before deleting the Session
2016-09-02 14:01:41 -05:00
Vedran Pavić
8db7d394ba Fix broken samples (#606) 2016-09-02 13:44:17 -05:00
Vedran Pavić
3e293e8b54 Polish JDBC configuration (#608) 2016-09-02 13:42:36 -05:00
Rob Winch
de7bb05fc1 Remove only master from .travis.yml 2016-08-30 09:16:34 -05:00
John Blum
3ee4c5b5d0 Register non-anonymous, named Instantiators for GemFireSession and GemFireSessionAttributes (#594)
Fixes gh-594 & gh-595
2016-08-17 17:35:00 -07:00
John Blum
acf37fc8f4 Fix deserialization issue caused by unregistered Instantiator (#595)
Fixes gh-594 & gh-595
2016-08-17 09:22:50 -07:00
John Blum
1256a94d7e Add Apache Geode support (#366)
Closes gh-365, gh-366
2016-08-15 14:53:12 -05:00
Vedran Pavić
bbb94361f8 Optimize save operation in JdbcOperationsSessionRepository (#582)
This commit improves saving of new sessions to only execute batch update operation if there really are any attributes to save.
2016-08-15 14:25:37 -05:00
John Blum
9af3f1fcec Enable named Pool configuration on cache client 'Session' Region (#487)
Fixes gh-484
2016-08-15 14:14:34 -05:00
Vedran Pavić
5b70d55a21 Improve result set extraction in JdbcOperationsSessionRepository (#577)
Previously, result set extraction was performed by a `RowMapper` implementation that required scrollable result sets support which caused compatibility issues with some database vendors.

This commit improves result set extraction by using a `ResultSetExtractor` which does not require scrollable result sets.
2016-08-15 13:34:12 -05:00
Vedran Pavić
9133e337e6 Use webjars-locator in samples (#587) 2016-08-15 13:25:53 -05:00
Rob Winch
9f36fd69ee Version is 1.3.0.BUILD-SNAPSHOT 2016-06-29 22:52:40 -05:00
Spring Buildmaster
e684f58403 Next development version 2016-06-30 03:05:34 +00:00
Spring Buildmaster
508e3e90a7 Release version 1.2.1.RELEASE 2016-06-30 03:05:19 +00:00
Vedran Pavic
0018a2f772 Add database schema drop scripts
Fixes gh-565
2016-06-29 21:21:53 -05:00
Rob Winch
e9f097db5c Gemfire Boot Sample uses Random Port
Previously, the Gemfire Boot Sample didn't select a random port for
Tomcat to run on.

Issue gh-496
2016-06-29 21:02:10 -05:00
Rob Winch
7e41e40762 Fix Gemfire ClientConfig BeanFactoryPostProcessor
@Bean method ClientConfig.propertySourcesPlaceholderConfigurer was
non-static and returns an object assignable to Spring's
BeanFactoryPostProcessor interface. This will result in a failure to
process annotations such as @Autowired, @Resource and @PostConstruct
within the method's declaring @Configuration class.

This commit makes the method static to avoid the problem.
2016-06-29 21:02:10 -05:00
Rob Winch
dc553104c6 Fix SessionRepositoryFilterTests doFilterLastAccessedTime assertion
Relax the assertion so that it is not exact to avoid timing issues.

Fixes gh-519
2016-06-29 16:24:52 -05:00
Vedran Pavic
2b4a349b1a Use webjars for static resources in sample apps 2016-06-29 12:59:54 -05:00
Rob Winch
4a1f132e07 Add JdbcOperationsSessionRepository overflow test
Add test for ensuring
JdbcOperationsSessionRepository.cleanupExpiredSessions does not have an
overflow when setting the max inactive interval to large values.

Issue gh-564
2016-06-29 11:58:18 -05:00
pasali
940555653c Fix overflow in JdbcOperationsSessionRepository
Changed maxInactiveIntervalSeconds to long in order to prevent integer
overflow in cleanUpExpiredSessions method.

Fixes gh-564
2016-06-29 11:56:03 -05:00
Rob Winch
82cb417910 Boot Gemfire Sample JDK7 compatible
Issue gh-496
2016-06-29 11:22:16 -05:00
Rob Winch
808a550343 Polish Boot Gemfire Sample
Issue gh-496
2016-06-29 11:08:05 -05:00
Rob Winch
cc08f83ce0 Fix Gemfire Eclipse & Checkstyle 2016-06-29 11:07:58 -05:00
John Blum
53c81de5e3 Add Spring Session Data GemFire with Spring Boot sample
Fixes gh-496
2016-06-29 10:34:58 -05:00
Vedran Pavić
dd268992a8 Update GitHub Pull Request template
Remove the CLA checkbox

Fixes gh-558
2016-06-21 14:04:39 -05:00
Eddú Meléndez
00004e2f50 Polish build.gradle samples
Disable publishing samples artifacts.

Fixes gh-518
2016-06-19 03:01:35 +10:00
Rob Winch
afb9a11abf Update to CLA tooling 2016-06-08 21:58:20 -05:00
Vedran Pavić
fb356f02ff SessionRepositoryFilterTests Sleeps for 50 ms 2016-06-06 08:45:19 -05:00
Vedran Pavić
509fb99a5d Improve JDBC transaction management doc 2016-05-20 11:56:58 -05:00
Vedran Pavić
2aff5b0377 Fix EnableRedisHttpSession.redisFlushMode javadoc
Fix EnableRedisHttpSession.redisFlushMode javadoc
2016-05-16 08:04:47 -05:00
Spring Buildmaster
016a007d12 Next development version 2016-05-12 01:28:55 +00:00
Spring Buildmaster
2a3ec2f4b9 Release version 1.2.0.RELEASE 2016-05-12 01:28:40 +00:00
Vedran Pavić
458ac6e700 Increase default size for attribute_name column (#515) 2016-05-06 17:03:40 -05:00
Vedran Pavić
33321d2e53 Polish MongoHttpSessionConfigurationTests
This improves test coverage and makes the MongoHttpSessionConfigurationTests
more consistent with analogous tests for other repositories.

Fixes gh-503
2016-05-02 15:11:54 -05:00
Spring Buildmaster
20130f8e8a Next development version 2016-04-27 01:19:07 +00:00
Spring Buildmaster
03f008f283 Release version 1.2.0.RC3 2016-04-27 01:18:46 +00:00
Vedran Pavic
00e7110594 Implement individual attribute persistence in JdbcOperationsSessionRepository 2016-04-26 14:29:23 -05:00
Eddú Meléndez Gonzales
f973e63fce Set collectionName attribute in MongoOperationsSessionRepository
Previous to this commit, collectionName could be set in
MongoHttpSessionConfiguration but it was never used. Now, attribute
can be set into MongoOperationsSessionRepository to take effect.

See gh-489
2016-04-26 14:27:02 -05:00
Eddú Meléndez
edbf8bf587 Expose attributes in MongoHttpSessionConfiguration
Attributes collectionName and maxInactiveIntervalInSeconds can be set
now.

Fixes gh-489
2016-04-20 13:49:18 -05:00
Johnny Lim
234f6c954a Remove duplicate words
Remove duplicate words

Fixes gh-494
2016-04-18 23:27:48 -05:00
Rob Winch
e0417523f6 Add tableName setter
Add tableName setter for JdbcHttpSessionConfiguration
2016-04-18 23:18:58 -05:00
Eddú Meléndez
5865b0a715 Add tableName setter for JdbcHttpSessionConfiguration
See gh-488
2016-04-19 01:00:40 +10:00
Vedran Pavić
67201417df Add compile dependency to commons-logging (#476) 2016-04-11 10:55:10 -05:00
Vedran Pavić
01a149737e Add GitHub issue/PR tempaltes
Add
2016-04-11 10:54:13 -05:00
John Blum
2d6f505a30 Update to Spring Data Hopper
Fixes gh-470
2016-04-11 09:31:05 -05:00
Rob Winch
7c616a1adf Redis save does nothing if nothing has changed
Fixes gh-467
2016-04-06 15:38:34 -05:00
Spring Buildmaster
61b01d9ecd Next development version 2016-04-06 14:26:42 +00:00
Spring Buildmaster
f9163d94fd Release version 1.2.0.RC2 2016-04-06 14:26:36 +00:00
Vedran Pavić
3f819a94b1 Enable transaction management for JdbcOperationsSessionRepository operations 2016-04-05 23:39:36 -05:00
Rob Winch
1e1d24895c Merge pull request #454 from vpavic/jdbc-boot-sample
Add JDBC Spring Boot sample
2016-04-04 08:36:27 -05:00
Rob Winch
e134a4cdb8 Merge pull request #463 from vpavic/fix-h2-console-urls
Make H2 console URL consistent across sample projects
2016-04-04 07:35:45 -05:00
Rob Winch
75006cd7dd Merge pull request #462 from vpavic/gh-457
Update WebSocket sample to use H2 console auto-configuration
2016-04-04 07:34:09 -05:00
Vedran Pavic
9c8f8894e1 Add JDBC Spring Boot sample 2016-04-03 20:57:16 +02:00
Vedran Pavic
99db45ea72 Make H2 console URL consistent across sample projects 2016-04-01 22:36:24 +02:00
Vedran Pavic
c07583bd47 Update WebSocket sample to use H2 console auto-configuration
Fixes gh-457.
2016-04-01 22:26:13 +02:00
Rob Winch
cce8dac4b7 Fix WebSocket AbstractMethodError
Fixes gh-460
2016-04-01 14:27:08 -05:00
Rob Winch
5bde226ecc Fix Eclipse compile errors
* Most web.xml servlet API versions updated to 3.0 for ASYNC support
* httpsession-xml is left at 2.5 to ensure compatability & remove ASYNC
* Remove @Override on interface override
2016-04-01 11:56:27 -05:00
Rob Winch
f8f6ee20c0 Externalize sample.gradle 2016-03-30 10:23:11 -05:00
Rob Winch
3bb96e8e82 Remove Wrapper from Gradle Sample
Issue gh-246
2016-03-30 10:22:59 -05:00
Rob Winch
b7367680cb Fix Package Names in Grails Sample
Issue gh-246
2016-03-30 10:22:59 -05:00
Rob Winch
a26a21b663 Add Grails 3 Sample to What's New
Issue gh-246
2016-03-30 09:28:03 -05:00
Rob Winch
3825a46418 Polish Grails 3 Sample
Issue gh-246
2016-03-29 20:58:26 -05:00
Eric Helgeson
779277b16d Add Grails Sample
Fixes gh-246
2016-03-29 20:58:26 -05:00
Rob Winch
b97306a83d Merge pull request #450 from vpavic/gh-445
Fix loading of JdbcSession's lastAccessedTime attribute
2016-03-28 15:06:49 -05:00
Rob Winch
6b13111079 Merge pull request #451 from vpavic/polish-tests
Polish JdbcHttpSessionConfigurationTests
2016-03-28 11:15:15 -05:00
Vedran Pavic
63006db45d Fix loading of JdbcSession's lastAccessedTime attribute
Fixes gh-445.
2016-03-28 18:13:16 +02:00
Vedran Pavic
0a99e065ff Polish JdbcHttpSessionConfigurationTests 2016-03-28 17:49:02 +02:00
Scott Carlson
bd2d846917 Add Dispatcher types to web.xml
Fixes gh-443
2016-03-28 09:16:13 -05:00
Rob Winch
79928bd7fe Merge pull request #436 from vpavic/improve-mongo-it
Use Flapdoodle Embedded MongoDB for integration tests
2016-03-25 09:30:40 -05:00
Vedran Pavic
903cac492e Use Flapdoodle Embedded MongoDB for integration tests and samples 2016-03-25 07:27:33 +01:00
Rob Winch
3980be349b Merge pull request #440 from lowzj/master_fix-typo
Fix SessionRepositoryFilter comment typo
2016-03-23 08:27:23 -05:00
lowzj
128e0c4d47 Fix SessionRepositoryFilter comment typo 2016-03-23 19:32:05 +08:00
Rob Winch
37bc6a352f Merge pull request #437 from vpavic/improve-build
Externalize H2 database dependency version
2016-03-20 11:10:24 -05:00
Vedran Pavic
b88f48f01d Externalize H2 database dependency version 2016-03-20 01:56:35 +01:00
Spring Buildmaster
028e277fc9 Next development version 2016-03-16 20:32:18 -07:00
Spring Buildmaster
904cdf4e65 Release version 1.2.0.RC1 2016-03-16 20:32:03 -07:00
Rob Winch
b58ea03a3b Polish JdkMongoSessionConverter
* Serializer/Deserializer -> Converter API
* Verify/Test null constructor arguments

Issue gh-431
2016-03-16 21:06:57 -05:00
Jakub Kubrynski
9014ac9060 JdkMongoSessionConverter supports custom ClassLoader
Fixes gh-431
2016-03-16 20:35:39 -05:00
Rob Winch
7a82915a98 Polish Mongo Documentation
* Externalize documentation code for testing
* Polish wording
* Start each new sentence with new line

Issue gh-430
2016-03-16 09:12:35 -05:00
Rob Winch
e16224583c Fix Eclipse copyright start date 2016-03-16 09:12:35 -05:00
Jakub Kubrynski
96861ea535 Extend Mongo documentation 2016-03-16 09:12:35 -05:00
Rob Winch
de3d04fb29 Merge pull request #429 from eddumelendez/gh-427
Update to Sonarqube Plugin
2016-03-15 08:41:02 -05:00
Eddú Meléndez
68823eec29 Update to Sonarqube Plugin
See gh-427
2016-03-15 07:59:18 +10:00
Rob Winch
a58d4f64fb Merge pull request #428 from vpavic/update-docs
Fix incorrect link in What's new section
2016-03-14 16:22:40 -05:00
Vedran Pavic
40ad82806c Fix incorrect link in What's new section 2016-03-14 22:08:30 +01:00
Rob Winch
6db7d45f65 Add gh-423 to What's New in 1.2
Issue gh-420
2016-03-14 13:34:38 -05:00
Rob Winch
bbbdf939e8 Polish JacksonMongoSessionConverter
Issue gh-416
2016-03-14 13:29:17 -05:00
Jakub Kubrynski
e23398b890 Add JacksonMongoSessionConverter
Fixes gh-416
2016-03-14 13:29:17 -05:00
Rob Winch
5cfbeae161 Add Travis Badge
Issue gh-394
2016-03-14 12:42:03 -05:00
Rob Winch
c6e71a90bb Add Travis
Fixes gh-394
2016-03-14 12:30:02 -05:00
Rob Winch
195228ff5a Fix checkstyle
Fixes gh-423
2016-03-14 10:41:40 -05:00
Øyvind Horneland
831d2f4152 SessionRepositoryFilter caches null session lookup
If a session cannot be found by id, we will cache that result for any
subsequent calls to getSession(false) for the duration of this request.

Fixes gh-423
2016-03-14 10:40:25 -05:00
Rob Winch
4f7728f5b5 Fix Checkstyle Eclipse Settings
Issue gh-417
2016-03-14 00:13:46 -05:00
Rob Winch
11e418526e Add What's new in 1.2
Fixes gh-420
2016-03-11 10:21:23 -06:00
Erin Drummond
49f24e493d Fix JdbcOperationsSessionRepository session cleanup
Make JDBC session cleanup logic actually cleanup expired sessions
as opposed to all sessions

Fixes gh-421
2016-03-11 09:28:18 -06:00
Vedran Pavic
bd6d5bf419 Add JDBC documentation
Fixes gh-415
2016-03-10 09:24:57 -06:00
Rob Winch
9b989c4a3e Merge pull request #414 from vpavic/fix-docs-test
Fix bad reference in IndexDocTests
2016-03-07 16:19:28 -06:00
Rob Winch
68ddcef83e Fix Javadoc 2016-03-07 16:08:54 -06:00
Rob Winch
2592905b41 Add Eclipse settings
Fixes gh-417
2016-03-07 15:37:22 -06:00
Rob Winch
4be47f6b40 Add Agregate checkstyle task
Issue gh-393
2016-03-07 15:37:12 -06:00
Rob Winch
35b5fcdc75 Refine Checkstyle
Refine the Checkstyle rules slightly

Fixes gh-393
2016-03-07 15:36:56 -06:00
Vedran Pavic
4d010b23b8 Add Checkstyle to build
Fixes gh-393
2016-03-07 15:36:44 -06:00
Rob Winch
f0200696ef Additional Checkstyle Fixes
Issue gh-393
2016-03-07 15:36:17 -06:00
Vedran Pavic
7f3302253b Prepare codebase to adhere to Checkstyle rules
Issue gh-393
2016-03-07 15:33:42 -06:00
Rob Winch
9e3bcafa75 Use @SpringBootApplication 2016-03-07 12:45:06 -06:00
Rob Winch
216bfd7355 MongoSessionConverter -> AbstractMongoSessionConverter
Using Abstract prefix follows conventions for abstract classes
beginning with Abstract. This also opens up the door to introduce
a MongoSessionConverter interface in the future.

Issue gh-17
2016-03-07 12:45:05 -06:00
Jakub Kubrynski
34cebc3df6 Add MongoOperationsSessionRepository
Fixes gh-17
2016-03-07 12:45:05 -06:00
Rob Winch
7b28b214ff Polish JdbcOperationsSessionRepository
* Fix whitepspaces
* Remove Override from interfaces

Issue gh-364
2016-03-07 09:25:30 -06:00
Rob Winch
8f8cfe5d79 JdbcOperationsSessionRepository uses ConversionService
Issue gh-364
2016-03-07 09:18:21 -06:00
Vedran Pavic
e0e29eab35 Fix bad reference in IndexDocTests 2016-03-06 23:18:51 +01:00
Vedran Pavic
cd38e307e0 Add JdbcOperationsSessionRepository
This commit provides implementation of SessionRepository based
on Spring's JdbcOperations interface.

@EnableJdbcHttpSession annotation is provided to ease the
configuration, together with spring-session-jdbc BOM and schema
creation scripts for all major databases.

Fixes gh-364
2016-03-04 11:24:06 -06:00
Rob Winch
091d0d8d9f DefaultCookieSerializer ignores null Cookie value
Previously a null Cookie value was returned by DefaultCookieSerializer
readCookieValues(). This could cause NullPointerExeptions later on.

This commit ignores cookies with a null value.

Fixes gh-392
2016-03-04 10:22:33 -06:00
John Blum
3300b58afe Fix GemFire TODOs
Upgrades Spring Session to use Spring Data GemFire 1.7.4.RELEASE
and addresses all TODOs.

Fixes gh-403
2016-02-29 21:07:00 -06:00
John Blum
00af8456d4 @DirtiesContext for GemFire integration tests
Applies @DirtiesContext to all Spring Session Data GemFire integration
tests to ensure that no lingering GemFire (peer or client) cache
instances cause conflicts on downstream tests.

Fixes gh-396
2016-02-29 20:46:59 -06:00
Rob Winch
aeb0add50e Fix GemFire compile in Eclipse
Fixes gh-399
2016-02-29 15:47:01 -06:00
Rob Winch
671bfa0444 Revert "Fix GemFire compile in Eclipse"
This reverts commit 8bf3a251bb.
since the fixes to the tests are incorrect.
2016-02-29 15:46:44 -06:00
Rob Winch
8bf3a251bb Fix GemFire compile in Eclipse
Fixes gh-399
2016-02-29 14:38:18 -06:00
Rob Winch
5d1c60711f Merge pull request #397 from vpavic/update-docs
Fix minor documentation errors
2016-02-29 12:59:41 -06:00
Vedran Pavic
32cf8ad7ad Fix minor documentation errors 2016-02-28 22:25:42 +01:00
Rob Winch
d3139b04e1 Update to 1.2.0.BUILD-SNAPSHOT 2016-02-25 14:40:17 -06:00
Spring Buildmaster
cc0bfc2299 Next development version 2016-02-25 11:15:24 -08:00
532 changed files with 22914 additions and 36812 deletions

3
.github/ISSUE_TEMPLATE.md vendored Normal file
View 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
View 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 #).
-->

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@ target
out
.springBeans
*.rdb
!eclispe/.checkstyle
.checkstyle

19
.travis.yml Normal file
View 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

View File

@@ -21,12 +21,6 @@ reproduces the problem.
== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you to sign the
https://support.springsource.com/spring_committer_signup[contributor's agreement].
If you have previously signed the CLA for any project, there is no need to sign up again.
Signing the contributor's agreement does not grant anyone commit rights to the main
repository, but it does mean that we can accept your contributions, and you will get an
author credit if we do. Active contributors might be asked to join the core team, and
given the ability to merge pull requests. Use the project `Spring Security` (the parent project)
and '`Rob Winch`' in the project lead field when you complete the form.
If you have not previously done so, please fill out and
submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement].

View File

@@ -1,5 +1,7 @@
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

View File

@@ -3,7 +3,7 @@ buildscript {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.5")
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')
@@ -12,12 +12,18 @@ buildscript {
}
}
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"
@@ -26,12 +32,11 @@ ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
apply plugin: 'sonar-runner'
apply plugin: 'base'
sonarRunner {
sonarProperties {
sonarqube {
properties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Session"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
@@ -64,4 +69,4 @@ task docsZip(type: Zip, dependsOn: 'configDocsZip') {
artifacts {
archives docsZip
}
}

View File

@@ -0,0 +1,166 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://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>

View File

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

View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://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>

View File

@@ -16,10 +16,6 @@ liveReload {
docRoot asciidoctor.sourceDir.canonicalPath
}
repositories {
maven { url 'http://dist.gemstone.com/maven/release' }
}
asciidoctorj {
}
@@ -28,11 +24,13 @@ 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-config:${springSecurityVersion}",
"org.springframework.security:spring-security-web:${springSecurityVersion}",
"org.springframework.security:spring-security-test:${springSecurityVersion}",
'junit:junit:4.11',
@@ -60,6 +58,7 @@ asciidoctor {
'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',

View 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 http://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 http://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 http://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.

View File

@@ -64,6 +64,8 @@ Ensure you have the following in your pom.xml:
----
endif::[]
// tag::config[]
[[security-spring-configuration]]
== Spring Configuration
@@ -79,7 +81,9 @@ 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.
<2> In order to support retrieval of sessions by principal name index, appropriate `ValueExtractor` needs to be registered.
Spring Session provides `PrincipalNameExtractor` for this purpose.
<3> 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 http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
@@ -88,8 +92,8 @@ For more information on configuring Hazelcast, refer to the http://docs.hazelcas
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.
In order for our `Filter` to do its magic, Spring needs to load our `SessionConfig` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our `SessionConfig` class to it.
.src/main/java/sample/SecurityInitializer.java
[source,java]
@@ -113,7 +117,7 @@ NOTE: The name of our class (Initializer) does not matter. What is important is
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`.
// end::config[]
[[hazelcast-spring-security-sample]]
== Hazelcast Spring Security Sample Application
@@ -188,4 +192,4 @@ For example, you could delete an individual key as follows (replacing `7e8383a4-
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.
Now observe that you are no longer authenticated with this session.

View 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
http://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 http://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 http://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 http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
with a GemFire http://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 http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
using a GemFire http://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 http://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 http://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.

View 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 http://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.

View 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 http://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 http://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 http://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.

View 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 http://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.

View 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 http://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.

View File

@@ -160,7 +160,7 @@ Specifically, we notice the following things about our response:
* 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 the username just as before:
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"

View File

@@ -1,5 +1,5 @@
= Spring Session
Rob Winch
Rob Winch, Vedran Pavić, Jakub Kubrynski
:doctype: book
:indexdoc-tests: {docs-test-dir}docs/IndexDocTests.java
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
@@ -22,21 +22,15 @@ Additional features include:
* <<websocket,WebSocket>> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
== What's New in 1.1
== What's New in 1.2
Below are the highlights of what is new in Spring Session 1.1. You can find a complete list of what's new in https://github.com/spring-projects/spring-session/issues?utf8=%E2%9C%93&q=milestone%3A%221.1.0+M1%22[1.1.0 M1] and https://github.com/spring-projects/spring-session/issues?utf8=%E2%9C%93&q=milestone%3A%221.1.0+RC1%22[1.1.0 RC1] by referring to the changelog.
Below are the highlights of what is new in Spring Session 1.2. You can find a complete list of what's new in https://github.com/spring-projects/spring-session/issues?utf8=%E2%9C%93&q=milestone%3A%221.2.0+RC1%22[1.2.0 RC1] by referring to the changelog.
* https://github.com/spring-projects/spring-session/issues/148[#148] - Added <<httpsession-gemfire,GemFire Support>>
* https://github.com/spring-projects/spring-session/issues/7[#7] - link:guides/findbyusername.html[Query by Username]
* https://github.com/spring-projects/spring-session/issues/299[#299] - link:guides/custom-cookie.html[Customize Cookie Creation]
* https://github.com/spring-projects/spring-session/issues/4[#4] - Add <<httpsession-httpsessionlistener,HttpSessionListener>> support
* https://github.com/spring-projects/spring-session/issues/283[#283] - Allow override default `RedisSerializer`
* https://github.com/spring-projects/spring-session/issues/277[#277] - Added link:guides/hazelcast-spring.html[@EnableHazelcastHttpSession]
* https://github.com/spring-projects/spring-session/issues/271[#271] - Performance improvements
* https://github.com/spring-projects/spring-session/pull/218[#218] - Allow scoping the session in Redis using <<api-redisoperationssessionrepository-config,redisNamespace>>
* https://github.com/spring-projects/spring-session/issues/273[#273] - Allow writing to Redis immediately (instead of lazily) using <<api-redisoperationssessionrepository-config,redisFlushMode>>
* https://github.com/spring-projects/spring-session/issues/272[#272] - Add `ExpiringSession.setLastAccessedTime(long)`
* https://github.com/spring-projects/spring-session/pull/349[#349] - Added https://gitter.im/spring-projects/spring-session[Gitter Room] for discussing Spring Session
* Added <<httpsession-jdbc,JdbcOperationsSessionRepository>> (See https://github.com/spring-projects/spring-session/issues/364[#364]).
* Added <<httpsession-mongo,MongoOperationsSessionRepository>> (See https://github.com/spring-projects/spring-session/pull/371[#371]).
* SessionRepositoryFilter caches null session lookup (See https://github.com/spring-projects/spring-session/issues/423[#423])
* link:guides/grails3.html[Grails 3 Sample & Guide]
* Improved Workspace Setup (See https://github.com/spring-projects/spring-session/pull/417[#417])
[[samples]]
== Samples and Guides (Start Here)
@@ -55,6 +49,10 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session to replace the `HttpSession` with a Redis store using XML based configuration.
| link:guides/httpsession-xml.html[HttpSession XML Guide]
| {gh-samples-url}httpsession-gemfire-boot[HttpSession with GemFire using Spring Boot]
| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a Client/Server topology in a Spring Boot application.
| link:guides/httpsession-gemfire-boot.html[HttpSession GemFire Boot Guide]
| {gh-samples-url}httpsession-gemfire-clientserver[HttpSession with GemFire (Client/Server)]
| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a Client/Server topology.
| link:guides/httpsession-gemfire-clientserver.html[HttpSession GemFire Client/Server Guide]
@@ -79,6 +77,10 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session with Spring Boot.
| link:guides/boot.html[Spring Boot Guide]
| {gh-samples-url}grails3[Grails 3]
| Demonstrates how to use Spring Session with Grails 3.
| link:guides/grails3.html[Grails 3 Guide]
| {gh-samples-url}security[Spring Security]
| Demonstrates how to use Spring Session with an existing Spring Security application.
| link:guides/security.html[Spring Security Guide]
@@ -99,6 +101,10 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session with WebSockets.
| link:guides/websocket.html[WebSocket Guide]
| {gh-samples-url}mongo[Mongo]
| Demonstrates how to use Spring Session with Mongo.
| link:guides/mongo.html[Mongo Guide]
[[samples-hazelcast]]
| {gh-samples-url}hazelcast[Hazelcast]
| Demonstrates how to use Spring Session with Hazelcast.
@@ -109,6 +115,18 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session and Hazelcast with an existing Spring Security application.
| link:guides/hazelcast-spring.html[Hazelcast Spring Guide]
| {gh-samples-url}httpsession-jdbc[HttpSession JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/httpsession-jdbc.html[HttpSession JDBC Guide]
| {gh-samples-url}httpsession-jdbc-xml[HttpSession JDBC XML]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store using XML based configuration.
| link:guides/httpsession-jdbc-xml.html[HttpSession JDBC XML Guide]
| {gh-samples-url}httpsession-jdbc-boot[HttpSession JDBC Spring Boot]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store when using Spring Boot.
| link:guides/httpsession-jdbc-boot.html[HttpSession JDBC Spring Boot Guide]
|===
[[httpsession]]
@@ -140,7 +158,7 @@ You can choose from enabling this using either:
This section describes how to use Redis to back `HttpSession` using Java based configuration.
NOTE: The <<samples, HttpSession Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using XML configuration.
NOTE: The <<samples, HttpSession Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession Guide when integrating with your own application.
include::guides/httpsession.adoc[tags=config,leveloffset=+3]
@@ -259,6 +277,109 @@ Guide when integrating with your own application.
include::guides/httpsession-gemfire-p2p-xml.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc]]
=== HttpSession with JDBC
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
You can choose from enabling this using either:
* <<httpsession-jdbc-jc,Java Based Configuration>>
* <<httpsession-jdbc-xml,XML Based Configuration>>
* <<httpsession-jdbc-boot,Spring Boot Based Configuration>>
[[httpsession-jdbc-jc]]
==== JDBC Java Based Configuration
This section describes how to use a relational database to back `HttpSession` using Java based configuration.
NOTE: The <<samples, HttpSession JDBC Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC Guide when integrating with your own application.
include::guides/httpsession-jdbc.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc-xml]]
==== JDBC XML Based Configuration
This section describes how to use a relational database to back `HttpSession` using XML based configuration.
NOTE: The <<samples, HttpSession JDBC XML Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using XML configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC XML Guide when integrating with your own application.
include::guides/httpsession-jdbc-xml.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc-boot]]
==== JDBC Spring Boot Based Configuration
This section describes how to use a relational database to back `HttpSession` when using Spring Boot.
NOTE: The <<samples, HttpSession JDBC Spring Boot Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Spring Boot.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession JDBC Spring Boot Guide when integrating with your own application.
include::guides/httpsession-jdbc-boot.adoc[tags=config,leveloffset=+3]
[[httpsession-mongo]]
=== HttpSession with Mongo
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
This section describes how to use Mongo to back `HttpSession` using Java based configuration.
NOTE: The <<samples, HttpSession Mongo Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed HttpSession Guide when integrating with your own application.
include::guides/mongo.adoc[tags=config,leveloffset=+3]
==== Session serialization mechanisms
To be able to persist session objects in MongoDB we need to provide the serialization/deserialization mechanism.
Depending on your classpath Spring Session will choose one of two build-in converters:
* `JacksonMongoSessionConverter` when `ObjectMapper` class is available, or
* `JdkMongoSessionConverter` otherwise.
===== JacksonMongoSessionConverter
This mechanism uses Jackson to serialize session objects to/from JSON.
`JacksonMongoSessionConverter` will be the default when Jackson is detected on the classpath and the user has not explicitly registered a `AbstractMongoSessionConverter` Bean.
If you would like to provide custom Jackson modules you can do it by explicitly registering `JacksonMongoSessionConverter`:
[source,java,indent=0]
----
include::{docs-test-dir}docs/http/MongoJacksonSessionConfiguration.java[tags=config]
----
==== JdkMongoSessionConverter
`JdkMongoSessionConverter` uses standard Java serialization to persist session attributes map to MongoDB in a binary form.
However, standard session elements like id, access time, etc are still written as a plain Mongo objects and can be read and queried without additional effort.
`JdkMongoSessionConverter` is used if Jackson is not on the classpath and no explicit `AbstractMongoSessionConverter` Bean has been defined.
You can explicitly register `JdkMongoSessionConverter` by defining it as a Bean.
[source,java,indent=0]
----
include::{docs-test-dir}docs/http/MongoJdkSessionConfiguration.java[tags=config]
----
There is also a constructor taking `Serializer` and `Deserializer` objects, allowing you to pass custom implementations, which is especially important when you want to use non-default classloader.
==== Using custom converters
You can create your own session converter by extending `AbstractMongoSessionConverter` class.
The implementation will be used for serializing, deserializing your objects and for providing queries to access the session.
[[httpsession-hazelcast]]
=== HttpSession with Hazelcast
Using Spring Session with `HttpSession` is enabled by adding a Servlet Filter before anything that uses the `HttpSession`.
This section describes how to use Hazelcast to back `HttpSession` using Java based configuration.
NOTE: The <<samples, Hazelcast Spring Sample>> provides a working sample on how to integrate Spring Session and `HttpSession` using Java configuration.
You can read the basic steps for integration below, but you are encouraged to follow along with the detailed Hazelcast Spring Guide when integrating with your own application.
include::guides/hazelcast-spring.adoc[tags=config,leveloffset=+2]
[[httpsession-how]]
=== How HttpSession Integration Works
@@ -396,6 +517,43 @@ Before using WebSocket integration, you should be sure that you have <<httpsessi
include::guides/websocket.adoc[tags=config,leveloffset=+2]
[[spring-security-concurrent-sessions]]
== Spring Security Integration
Spring Session provides integration with Spring Security to support its concurrent session control.
This allows limiting the number of active sessions that a single user can have concurrently, but unlike the default
Spring Security support this will also work in a clustered environment. This is done by providing a custom
implementation of Spring Security's `SessionRegistry` interface.
[[spring-security-concurrent-sessions-how]]
=== Configuring Spring Security's concurrent session management
When using Spring Security's Java config DSL, you can configure the custom `SessionRegistry` through the
`SessionManagementConfigurer` like this:
[source,java,indent=0]
----
include::{docs-test-dir}docs/security/SecurityConfiguration.java[tags=class]
----
This assumes that you've also configured Spring Session to provide a `FindByIndexNameSessionRepository` that
returns `ExpiringSession` instances.
When using XML configuration, it would look something like this:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/security/security-config.xml[tags=config]
----
This assumes that your Spring Session `SessionRegistry` bean is called `sessionRegistry`, which is the name used by all
`SpringHttpSessionConfiguration` subclasses except for the one for MongoDB: there it's called `mongoSessionRepository`.
[[spring-security-concurrent-sessions-limitations]]
=== Limitations
Spring Session's implementation of Spring Security's `SessionRegistry` interface does not support the `getAllPrincipals`
method, as this information cannot be retrieved using Spring Session. This method is never called by Spring Security,
so this only affects applications that access the `SessionRegistry` themselves.
[[api]]
== API Documentation
@@ -501,44 +659,6 @@ It is important to note that no infrastructure for session expirations is config
This is because things like session expiration are highly implementation dependent.
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
[[api-enablehazelcasthttpsession]]
=== EnableHazelcastHttpSession
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
You must provide a single `HazelcastInstance` bean for the configuration to work.
For example:
[source,java,indent=0]
----
include::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
----
This will configure Hazelcast in embedded mode with default configuration.
See the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation] for
detailed information on configuration options for Hazelcast.
[[api-enablehazelcasthttpsession-storage]]
==== Storage Details
Sessions will be stored in a distributed `Map` in Hazelcast using a <<api-mapsessionrepository,MapSessionRepository>>.
The `Map` interface methods will be used to `get()` and `put()` Sessions.
The expiration of a session in the `Map` is handled by Hazelcast's support for setting the time to live on an entry when it is `put()` into the `Map`. Entries (sessions) that have been idle longer than the time to live will be automatically removed from the `Map`.
You shouldn't need to configure any settings such as `max-idle-seconds` or `time-to-live-seconds` for the `Map` within the Hazelcast configuration.
[[api-enablehazelcasthttpsession-customize]]
==== Basic Customization
You can use the following attributes on `@EnableHazelcastHttpSession` to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds. Default is 1800 seconds (30 minutes)
* **sessionMapName** - the name of the distributed `Map` that will be used in Hazelcast to store the session data.
[[api-enablehazelcasthttpsession-events]]
==== Session Events
Using a `MapListener` to respond to entries being added, evicted, and removed from the distributed `Map`, these events will trigger
publishing SessionCreatedEvent, SessionExpiredEvent, and SessionDeletedEvent events respectively using the `ApplicationEventPublisher`.
[[api-redisoperationssessionrepository]]
=== RedisOperationsSessionRepository
@@ -728,7 +848,7 @@ For example, Java Configuration can use the following:
include::{docs-test-dir}docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java[tags=configure-redis-action]
----
XML Configuraiton can use the following:
XML Configuration can use the following:
[source,xml,indent=0]
----
@@ -890,8 +1010,121 @@ To run it use the following:
./gradlew :samples:hazelcast-spring:tomcatRun
[[api-mapsessionrepository-hazelcast]]
==== Using Spring Session and Hazlecast
[[api-jdbcoperationssessionrepository]]
=== JdbcOperationsSessionRepository
`JdbcOperationsSessionRepository` is a `SessionRepository` implementation that uses Spring's `JdbcOperations` to store sessions in a relational database.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
Please note that this implementation does not support publishing of session events.
[[api-jdbcoperationssessionrepository-new]]
==== Instantiating a JdbcOperationsSessionRepository
A typical example of how to create a new instance can be seen below:
[source,java,indent=0]
----
include::{indexdoc-tests}[tags=new-jdbcoperationssessionrepository]
----
For additional information on how to create and configure `JdbcTemplate` and `PlatformTransactionManager`, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
[[api-jdbcoperationssessionrepository-config]]
==== EnableJdbcHttpSession
In a web environment, the simplest way to create a new `JdbcOperationsSessionRepository` is to use `@EnableJdbcHttpSession`.
Complete example usage can be found in the <<samples>>
You can use the following attributes to customize the configuration:
* **tableName** - the name of database table used by Spring Session to store sessions
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds
===== Custom LobHandler
You can customize the BLOB handling by creating a Bean named `springSessionLobHandler` that implements `LobHandler`.
===== Custom ConversionService
You can customize the default serialization and deserialization of the session by providing a `ConversionService` instance.
When working in a typical Spring environment, the default `ConversionService` Bean (named `conversionService`) will be automatically picked up and used for serialization and deserialization.
However, you can override the default `ConversionService` by providing a Bean named `springSessionConversionService`.
[[api-jdbcoperationssessionrepository-storage]]
==== Storage Details
By default, this implementation uses `SPRING_SESSION` and `SPRING_SESSION_ATTRIBUTES` tables to store sessions.
Note that the table name can be easily customized as already described. In that case the table used to store attributes will be named using the provided table name, suffixed with `_ATTRIBUTES`.
If further customizations are needed, SQL queries used by the repository can be customized using `set*Query` setter methods. In this case you need to manually configure the `sessionRepository` bean.
Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database.
Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type.
For example, with PostgreSQL database you would use the following schema script:
[source,sql,indent=0]
----
include::{session-main-resources-dir}org/springframework/session/jdbc/schema-postgresql.sql[]
----
And with MySQL database:
[source,sql,indent=0]
----
include::{session-main-resources-dir}org/springframework/session/jdbc/schema-mysql.sql[]
----
==== Transaction management
All JDBC operations in `JdbcOperationsSessionRepository` are executed in a transactional manner.
Transactions are executed with propagation set to `REQUIRES_NEW` in order to avoid unexpected behavior due to interference with existing transactions (for example, executing `save` operation in a thread that already participates in a read-only transaction).
[[api-hazelcastsessionrepository]]
=== HazelcastSessionRepository
`HazelcastSessionRepository` is a `SessionRepository` implementation that stores sessions in Hazelcast's distributed `IMap`.
In a web environment, this is typically used in combination with `SessionRepositoryFilter`.
[[api-hazelcastsessionrepository-new]]
==== Instantiating a HazelcastSessionRepository
A typical example of how to create a new instance can be seen below:
[source,java,indent=0]
----
include::{indexdoc-tests}[tags=new-hazelcastsessionrepository]
----
For additional information on how to create and configure Hazelcast instance, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
[[api-enablehazelcasthttpsession]]
==== EnableHazelcastHttpSession
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
You must provide a single `HazelcastInstance` bean for the configuration to work.
Complete configuration example can be found in the <<samples>>
[[api-enablehazelcasthttpsession-storage]]
==== Storage Details
Sessions will be stored in a distributed `IMap` in Hazelcast using a <<api-mapsessionrepository,MapSessionRepository>>.
The `IMap` interface methods will be used to `get()` and `put()` Sessions.
Additionally, `values()` method is used to support `FindByIndexNameSessionRepository#findByIndexNameAndIndexValue` operation, together with appropriate `ValueExtractor` that needs to be registered with Hazelcast. Refer to <<samples, Hazelcast Spring Sample>> for more details on this configuration.
The expiration of a session in the `IMap` is handled by Hazelcast's support for setting the time to live on an entry when it is `put()` into the `IMap`. Entries (sessions) that have been idle longer than the time to live will be automatically removed from the `IMap`.
You shouldn't need to configure any settings such as `max-idle-seconds` or `time-to-live-seconds` for the `IMap` within the Hazelcast configuration.
[[api-enablehazelcasthttpsession-customize]]
==== Basic Customization
You can use the following attributes on `@EnableHazelcastHttpSession` to customize the configuration:
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds. Default is 1800 seconds (30 minutes)
* **sessionMapName** - the name of the distributed `Map` that will be used in Hazelcast to store the session data.
[[api-enablehazelcasthttpsession-events]]
==== Session Events
Using a `MapListener` to respond to entries being added, evicted, and removed from the distributed `Map`, these events will trigger
publishing SessionCreatedEvent, SessionExpiredEvent, and SessionDeletedEvent events respectively using the `ApplicationEventPublisher`.
[[community]]
== Spring Session Community

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,8 +16,6 @@
package docs;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
@@ -27,14 +25,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
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 com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.DataPolicy;
@@ -46,12 +36,23 @@ 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.
* 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
@@ -61,27 +62,27 @@ import com.gemstone.gemfire.cache.server.CacheServer;
* @see com.gemstone.gemfire.cache.Region
* @see com.gemstone.gemfire.cache.client.ClientCache
* @see com.gemstone.gemfire.cache.server.CacheServer
* @since 1.1.0
*/
public class AbstractGemFireIntegrationTests {
public static final String GEMFIRE_LOG_LEVEL = System.getProperty(
"spring.session.data.gemfire.log-level", "warning");
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 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 long DEFAULT_WAIT_INTERVAL = 500L;
protected static final File WORKING_DIRECTORY = new File(System.getProperty("user.dir"));
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");
protected static final String GEMFIRE_LOG_FILE_NAME = System
.getProperty("spring.session.data.gemfire.log-file", "server.log");
@Autowired
protected Cache gemfireCache;
@@ -91,15 +92,17 @@ public class AbstractGemFireIntegrationTests {
@Before
public void setup() {
System.setProperty("gemfire.Query.VERBOSE", String.valueOf(isQueryDebuggingEnabled()));
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();
assertThat(directory.isDirectory() || directory.mkdirs())
.as(String.format("Failed to create directory (%1$s)", directory))
.isTrue();
directory.deleteOnExit();
@@ -107,7 +110,8 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static List<String> createJavaProcessCommandLine(Class<?> type, String... args) {
protected static List<String> createJavaProcessCommandLine(Class<?> type,
String... args) {
List<String> commandLine = new ArrayList<String>();
String javaHome = System.getProperty("java.home");
@@ -118,7 +122,8 @@ public class AbstractGemFireIntegrationTests {
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
.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"));
@@ -157,11 +162,10 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static Process run(Class<?> type, File directory, String... args) throws IOException {
return new ProcessBuilder()
.command(createJavaProcessCommandLine(type, args))
.directory(directory)
.start();
protected static Process run(Class<?> type, File directory, String... args)
throws IOException {
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args))
.directory(directory).start();
}
/* (non-Javadoc) */
@@ -170,8 +174,10 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
protected static boolean waitForCacheServerToStart(CacheServer cacheServer,
long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(),
cacheServer.getPort(), duration);
}
/* (non-Javadoc) */
@@ -180,7 +186,8 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
protected static boolean waitForCacheServerToStart(final String host, final int port,
long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
@@ -204,7 +211,8 @@ public class AbstractGemFireIntegrationTests {
}, duration);
}
// NOTE this method would not be necessary except Spring Sessions' build does not fork the test JVM
// 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() {
@@ -239,7 +247,8 @@ public class AbstractGemFireIntegrationTests {
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static boolean waitForProcessToStart(Process process, File directory, long duration) {
protected static boolean waitForProcessToStart(Process process, File directory,
long duration) {
final File processControl = new File(directory, DEFAULT_PROCESS_CONTROL_FILENAME);
waitOnCondition(new Condition() {
@@ -257,7 +266,8 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static int waitForProcessToStop(Process process, File directory, long duration) {
protected static int waitForProcessToStop(Process process, File directory,
long duration) {
final long timeout = (System.currentTimeMillis() + duration);
try {
@@ -312,24 +322,30 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected void assertRegion(Region<?, ?> actualRegion, String expectedName, DataPolicy expectedDataPolicy) {
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.getFullPath())
.isEqualTo(GemFireUtils.toRegionPath(expectedName));
assertThat(actualRegion.getAttributes()).isNotNull();
assertThat(actualRegion.getAttributes().getDataPolicy()).isEqualTo(expectedDataPolicy);
assertThat(actualRegion.getAttributes().getDataPolicy())
.isEqualTo(expectedDataPolicy);
}
/* (non-Javadoc) */
protected void assertIndex(Index index, String expectedExpression, String expectedFromClause) {
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);
protected void assertEntryIdleTimeout(Region<?, ?> region,
ExpirationAction expectedAction, int expectedTimeout) {
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(),
expectedAction, expectedTimeout);
}
/* (non-Javadoc) */
@@ -356,7 +372,7 @@ public class AbstractGemFireIntegrationTests {
List<String> regionList = new ArrayList<String>(regions.size());
for (Region<?,?> region : regions) {
for (Region<?, ?> region : regions) {
regionList.add(region.getFullPath());
}
@@ -366,7 +382,7 @@ public class AbstractGemFireIntegrationTests {
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected <T extends ExpiringSession> T createSession() {
T expiringSession = (T) sessionRepository.createSession();
T expiringSession = (T) this.sessionRepository.createSession();
assertThat(expiringSession).isNotNull();
return expiringSession;
}
@@ -381,19 +397,19 @@ public class AbstractGemFireIntegrationTests {
/* (non-Javadoc) */
protected <T extends ExpiringSession> T expire(T session) {
session.setLastAccessedTime(0l);
session.setLastAccessedTime(0L);
return session;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected <T extends ExpiringSession> T get(String sessionId) {
return (T) sessionRepository.getSession(sessionId);
return (T) this.sessionRepository.getSession(sessionId);
}
/* (non-Javadoc) */
protected <T extends ExpiringSession> T save(T session) {
sessionRepository.save(session);
this.sessionRepository.save(session);
return session;
}
@@ -404,13 +420,14 @@ public class AbstractGemFireIntegrationTests {
}
/**
* The SessionEventListener class is a Spring {@link ApplicationListener} listening for Spring HTTP Session
* application events.
* 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> {
public static class SessionEventListener
implements ApplicationListener<AbstractSessionEvent> {
private volatile AbstractSessionEvent sessionEvent;
@@ -424,14 +441,14 @@ public class AbstractGemFireIntegrationTests {
/* (non-Javadoc) */
public void onApplicationEvent(AbstractSessionEvent event) {
sessionEvent = event;
this.sessionEvent = event;
}
/* (non-Javadoc) */
public <T extends AbstractSessionEvent> T waitForSessionEvent(long duration) {
waitOnCondition(new Condition() {
public boolean evaluate() {
return (sessionEvent != null);
return (SessionEventListener.this.sessionEvent != null);
}
}, duration);
@@ -440,7 +457,8 @@ public class AbstractGemFireIntegrationTests {
}
/**
* The Condition interface defines a logical condition that must be satisfied before it is safe to proceed.
* The Condition interface defines a logical condition that must be satisfied before
* it is safe to proceed.
*/
protected interface Condition {
boolean evaluate();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import static org.assertj.core.api.Assertions.assertThat;
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;
@@ -36,7 +37,7 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import docs.AbstractGemFireIntegrationTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
@@ -59,7 +60,8 @@ public class HttpSessionGemFireIndexingITests extends AbstractGemFireIntegration
sessionRepository.save(session);
// tag::findbyindexname-get[]
Map<String,ExpiringSession> idToSessions = sessionRepository.findByIndexNameAndIndexValue(indexName, username);
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, username);
// end::findbyindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());
@@ -77,12 +79,15 @@ public class HttpSessionGemFireIndexingITests extends AbstractGemFireIntegration
Authentication authentication = context.getAuthentication();
// end::findbyspringsecurityindexname-context[]
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, 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());
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, authentication.getName());
// end::findbyspringsecurityindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());
@@ -110,9 +115,7 @@ public class HttpSessionGemFireIndexingITests extends AbstractGemFireIntegration
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -13,16 +13,17 @@
* 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;
import docs.AbstractGemFireIntegrationTests;
/**
* @author Rob Winch
*
@@ -30,7 +31,7 @@ import docs.AbstractGemFireIntegrationTests;
// tag::class-start[]
@EnableGemFireHttpSession(indexableSessionAttributes = { "name1", "name2", "name3" })
public class GemFireHttpSessionConfig {
// end::class-start[]
// end::class-start[]
@Bean
Properties gemfireProperties() {
@@ -38,7 +39,8 @@ public class GemFireHttpSessionConfig {
gemfireProperties.setProperty("name", GemFireHttpSessionConfig.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", AbstractGemFireIntegrationTests.GEMFIRE_LOG_LEVEL);
gemfireProperties.setProperty("log-level",
AbstractGemFireIntegrationTests.GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@@ -48,9 +50,7 @@ public class GemFireHttpSessionConfig {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -13,27 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http.gemfire.indexablesessionattributes;
import static org.assertj.core.api.Assertions.assertThat;
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 docs.AbstractGemFireIntegrationTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=GemFireHttpSessionConfig.class)
public class HttpSessionGemFireIndexingCustomITests extends AbstractGemFireIntegrationTests {
@ContextConfiguration(classes = GemFireHttpSessionConfig.class)
public class HttpSessionGemFireIndexingCustomITests
extends AbstractGemFireIntegrationTests {
@Test
public void findByIndexName() {
@@ -48,7 +50,8 @@ public class HttpSessionGemFireIndexingCustomITests extends AbstractGemFireInteg
sessionRepository.save(session);
// tag::findbyindexname-get[]
Map<String,ExpiringSession> idToSessions = sessionRepository.findByIndexNameAndIndexValue(indexName, attrValue);
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, attrValue);
// end::findbyindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs;
import java.util.Map;
@@ -21,6 +22,7 @@ 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;
@@ -39,7 +41,8 @@ public class FindByIndexNameSessionRepositoryTests {
public void setUsername() {
// tag::set-username[]
String username = "username";
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
this.session.setAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
// end::set-username[]
}
@@ -48,8 +51,10 @@ public class FindByIndexNameSessionRepositoryTests {
public void findByUsername() {
// tag::findby-username[]
String username = "username";
Map<String,Session> sessionIdToSession =
sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
Map<String, Session> sessionIdToSession = this.sessionRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
// end::findby-username[]
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.mock;
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;
@@ -28,6 +27,9 @@ 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
*/
@@ -40,10 +42,9 @@ public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
@Test
public void redisConnectionFactoryNotUsedSinceNoValidation() {
assertThat(filter).isNotNull();
assertThat(this.filter).isNotNull();
}
static RedisConnectionFactory connectionFactory() {
return mock(RedisConnectionFactory.class);
}

View File

@@ -1,42 +1,55 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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 static org.assertj.core.api.Assertions.*;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
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.MapSession;
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.hazelcast.HazelcastSessionRepository;
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() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
RepositoryDemo<ExpiringSession> demo = new RepositoryDemo<ExpiringSession>();
demo.repository = new MapSessionRepository();
demo.demo();
@@ -47,15 +60,15 @@ public class IndexDocTests {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = repository.createSession(); // <2>
S toSave = this.repository.createSession(); // <2>
// <3>
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
repository.save(toSave); // <4>
this.repository.save(toSave); // <4>
S session = repository.getSession(toSave.getId()); // <5>
S session = this.repository.getSession(toSave.getId()); // <5>
// <6>
User user = session.getAttribute(ATTR_USER);
@@ -66,7 +79,6 @@ public class IndexDocTests {
}
// end::repository-demo[]
@Test
public void expireRepositoryDemo() {
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<ExpiringSession>();
@@ -75,19 +87,18 @@ public class IndexDocTests {
demo.demo();
}
@SuppressWarnings("unused")
// tag::expire-repository-demo[]
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
private SessionRepository<S> repository; // <1>
public void demo() {
S toSave = repository.createSession(); // <2>
S toSave = this.repository.createSession(); // <2>
// ...
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
repository.save(toSave); // <4>
this.repository.save(toSave); // <4>
S session = repository.getSession(toSave.getId()); // <5>
S session = this.repository.getSession(toSave.getId()); // <5>
// ...
}
@@ -100,8 +111,8 @@ public class IndexDocTests {
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
JedisConnectionFactory factory = new JedisConnectionFactory();
SessionRepository<? extends ExpiringSession> repository =
new RedisOperationsSessionRepository(factory);
SessionRepository<? extends ExpiringSession> repository = new RedisOperationsSessionRepository(
factory);
// end::new-redisoperationssessionrepository[]
}
@@ -113,6 +124,42 @@ public class IndexDocTests {
// 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
@SuppressWarnings("unused")
public void newHazelcastSessionRepository() {
// tag::new-hazelcastsessionrepository[]
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
IMap<String, MapSession> sessions = hazelcastInstance
.getMap("spring:session:sessions");
HazelcastSessionRepository repository =
new HazelcastSessionRepository(sessions);
// end::new-hazelcastsessionrepository[]
}
@Test
public void runSpringHttpSessionConfig() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
@@ -122,12 +169,14 @@ public class IndexDocTests {
try {
context.getBean(SessionRepositoryFilter.class);
} finally {
}
finally {
context.close();
}
}
private static class User {
private User(String username) {}
private static final class User {
private User(String username) {
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs;
import static org.mockito.Mockito.mock;
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;
@@ -28,6 +28,8 @@ 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
*/
@@ -37,7 +39,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
@Test
public void redisConnectionFactoryNotUsedSinceNoValidation() {}
public void redisConnectionFactoryNotUsedSinceNoValidation() {
}
@EnableRedisHttpSession
@Configuration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs;
import org.springframework.context.annotation.Bean;
@@ -29,4 +30,4 @@ public class SpringHttpSessionConfig {
return new MapSessionRepository();
}
}
// end::class[]
// end::class[]

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
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;
@@ -32,6 +30,10 @@ 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
@@ -49,32 +51,38 @@ public abstract class AbstractHttpSessionListenerTests {
public void springSessionDestroyedTranslatedToSpringSecurityDestroyed() {
Session session = new MapSession();
publisher.publishEvent(new org.springframework.session.events.SessionDestroyedEvent(this, session));
this.publisher.publishEvent(
new org.springframework.session.events.SessionDestroyedEvent(this,
session));
assertThat(listener.getEvent().getId()).isEqualTo(session.getId());
assertThat(this.listener.getEvent().getId()).isEqualTo(session.getId());
}
static RedisConnectionFactory createMockRedisConnection() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
when(factory.getConnection()).thenReturn(connection);
given(factory.getConnection()).willReturn(connection);
return factory;
}
static class SecuritySessionDestroyedListener implements ApplicationListener<SessionDestroyedEvent> {
static class SecuritySessionDestroyedListener
implements ApplicationListener<SessionDestroyedEvent> {
private SessionDestroyedEvent event;
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
/*
* (non-Javadoc)
*
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.
* springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(SessionDestroyedEvent event) {
this.event = event;
}
public SessionDestroyedEvent getEvent() {
return event;
return this.event;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,24 +13,41 @@
* 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.config.MapAttributeConfig;
import com.hazelcast.config.MapIndexConfig;
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.HazelcastSessionRepository;
import org.springframework.session.hazelcast.PrincipalNameExtractor;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
//tag::config[]
@EnableHazelcastHttpSession // <1>
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new Config();
return Hazelcast.newHazelcastInstance(hazelcastConfig); // <2>
public HazelcastInstance hazelcastInstance() {
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.getMapConfig("spring:session:sessions") // <2>
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config); // <3>
}
}
// end::config[]
// end::config[]

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import org.springframework.context.annotation.Bean;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import org.springframework.test.context.ContextConfiguration;

View File

@@ -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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package 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[]

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,27 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.config;
import org.h2.server.web.WebServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
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;
/**
* Initializes the H2 {@link WebServlet} so we can access our in memory database
* from the URL "/h2".
*
* @author Jakub Kubrynski
* @author Rob Winch
*/
// tag::config[]
@Configuration
public class H2Initializer {
@EnableMongoHttpSession
public class MongoJdkSessionConfiguration {
@Bean
public ServletRegistrationBean h2Servlet() {
ServletRegistrationBean servletBean = new ServletRegistrationBean();
servletBean.addUrlMappings("/h2/*");
servletBean.setServlet(new WebServlet());
return servletBean;
public AbstractMongoSessionConverter mongoSessionConverter() {
return new JdkMongoSessionConverter();
}
}
// end::config[]

View 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import com.fasterxml.jackson.databind.module.SimpleModule;
class MyJacksonModule extends SimpleModule {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import org.springframework.context.annotation.Bean;
@@ -32,4 +33,4 @@ public class RedisHttpSessionConfig {
// ...
}
// end::config[]
// end::config[]

View File

@@ -0,0 +1,52 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
/**
* @author Joris Kuipers
*/
// tag::class[]
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// other config goes here...
.sessionManagement()
.maximumSessions(2)
.sessionRegistry(sessionRegistry());
}
@Bean
SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(this.sessionRepository);
}
}
// end::class[]

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;
@@ -23,18 +24,16 @@ import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBr
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author Rob Winch
*/
* @author Rob Winch
*/
// tag::class[]
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractWebSocketMessageBrokerConfigurer {
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages")
.withSockJS();
registry.addEndpoint("/messages").withSockJS();
}
@Override
@@ -43,4 +42,4 @@ public class WebSocketConfig
registry.setApplicationDestinationPrefixes("/app");
}
}
// end::class[]
// end::class[]

View File

@@ -19,4 +19,4 @@
<bean class="docs.HttpSessionConfigurationNoOpConfigureRedisActionXmlTests"
factory-method="connectionFactory"/>
</beans>
</beans>

View File

@@ -19,4 +19,4 @@
<bean class="docs.http.AbstractHttpSessionListenerTests"
factory-method="createMockRedisConnection"/>
<bean class="docs.http.AbstractHttpSessionListenerTests$SecuritySessionDestroyedListener"/>
</beans>
</beans>

View File

@@ -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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<!-- tag::config[] -->
<security:http>
<!-- other config goes here... -->
<security:session-management>
<security:concurrency-control max-sessions="2" session-registry-ref="sessionRegistry"
</security:session-management>
</security:http>
<bean id="sessionRegistry"
class="org.springframework.session.security.SpringSessionBackedSessionRegistry">
<constructor-arg ref="sessionRepository"/>
</bean>
<!-- end::config[] -->
</beans>

11
eclipse/.checkstyle Normal file
View 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>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring" 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"/>
@@ -13,7 +13,7 @@
<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="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"/>
@@ -31,9 +31,9 @@
<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="80"/>
<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="true"/>
<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"/>
@@ -65,7 +65,7 @@
<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="false"/>
<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"/>
@@ -79,7 +79,7 @@
<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="insert"/>
<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"/>
@@ -133,7 +133,7 @@
<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="1"/>
<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"/>
@@ -161,7 +161,7 @@
<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="0"/>
<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"/>
@@ -175,9 +175,9 @@
<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="1"/>
<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="1"/>
<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"/>
@@ -202,7 +202,7 @@
<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="true"/>
<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"/>
@@ -266,7 +266,7 @@
<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="1"/>
<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"/>
@@ -283,7 +283,7 @@
<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="false"/>
<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"/>

View 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

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +1,29 @@
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.1.0.RELEASE
springDataRedisVersion=1.6.2.RELEASE
version=1.3.0.M2
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
hazelcastVersion=3.6.5
springDataGeodeVersion=1.0.0.APACHE-GEODE-INCUBATING-M2
seleniumVersion=2.52.0
springSecurityVersion=4.0.3.RELEASE
springVersion=4.2.5.RELEASE
httpClientVersion=4.5.1
jedisVersion=2.7.3
jedisVersion=2.8.1
h2Version=1.4.192
springDataMongoVersion=1.9.1.RELEASE
springShellVersion=1.1.0.RELEASE
springDataGemFireVersion=1.7.2.RELEASE
assertjVersion=2.3.0
springDataGemFireVersion=1.8.1.RELEASE
assertjVersion=2.5.0
spockVersion=1.0-groovy-2.4
webjarsTaglibVersion=0.3
jstlVersion=1.2.1
groovyVersion=2.4.4

50
gradle/ide.gradle Normal file
View 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)

View File

@@ -6,6 +6,8 @@ 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'
@@ -75,6 +77,16 @@ task integrationTest(type: Test, dependsOn: jar) {
}
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 ]
@@ -83,4 +95,4 @@ eclipse {
project.idea.module {
scopes.TEST.plus += [project.configurations.integrationTestRuntime]
}
}

4
gradle/sample.gradle Normal file
View File

@@ -0,0 +1,4 @@
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarqube {
skipProject = true
}

View File

@@ -3,19 +3,18 @@ buildscript {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.5")
}
}
apply plugin: 'war'
apply plugin: 'tomcat'
apply plugin: 'com.bmuschko.tomcat'
[tomcatRun,tomcatRunWar]*.contextPath = '/'
task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
task integrationTomcatRun(type: com.bmuschko.gradle.tomcat.tasks.TomcatRun) {
onlyIf { !sourceSets.integrationTest.allSource.empty }
buildscriptClasspath = tomcatRun.buildscriptClasspath
contextPath = tomcatRun.contextPath
daemon = true
tomcatClasspath = tomcatRun.tomcatClasspath
@@ -36,7 +35,7 @@ task integrationTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) {
}
}
task integrationTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) {
task integrationTomcatStop(type: com.bmuschko.gradle.tomcat.tasks.TomcatStop) {
onlyIf { !sourceSets.integrationTest.allSource.empty }
doFirst {
stopPort = integrationTomcatRun.stopPort
@@ -61,4 +60,4 @@ def reservePorts(int count) {
def result = sockets*.localPort
sockets*.close()
result
}
}

View File

@@ -3,8 +3,6 @@ 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'
}
}
"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
}

View File

@@ -10,8 +10,7 @@ buildscript {
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
apply from: SAMPLE_GRADLE
group = 'samples'
@@ -21,6 +20,10 @@ dependencies {
"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.webjars:webjars-locator",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion"
@@ -50,4 +53,4 @@ def reservePort() {
def result = socket.localPort
socket.close()
result
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample
import geb.spock.*
@@ -71,4 +72,4 @@ class BootTests extends GebReportingSpec {
then: 'sent to the log in page'
at LoginPage
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.pages
import geb.*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.pages
import geb.*

View File

@@ -1,31 +1,28 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Rob Winch
*/
@Configuration
@ComponentScan
@EnableAutoConfiguration
@SpringBootApplication
public class Application {
public static void main(String[] args) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,12 +13,13 @@
* 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[]
public class HttpSessionConfig {
}
// end::class[]

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;
@@ -28,8 +29,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;

View File

@@ -1,2 +1,2 @@
spring.thymeleaf.cache=false
spring.template.cache=false
spring.template.cache=false

View File

@@ -8,4 +8,4 @@
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
</div>
</body>
</html>
</html>

View File

@@ -5,7 +5,7 @@
<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="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
@@ -65,11 +65,11 @@
margin-left: 1em;
}
</style>
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<![endif]-->
</head>
@@ -82,7 +82,7 @@
<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}">
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" />
@@ -119,4 +119,4 @@
</div>
</div>
</body>
</html>
</html>

View File

@@ -1,14 +1,12 @@
apply from: JAVA_GRADLE
apply from: TOMCAT_7_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
}
apply from: SAMPLE_GRADLE
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
@@ -16,4 +14,4 @@ dependencies {
testCompile "junit:junit:$junitVersion"
integrationTestCompile gebDependencies
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample
import geb.spock.*
@@ -42,4 +43,4 @@ class AttributeTests extends GebReportingSpec {
attributes[0].name == 'a'
attributes[0].value == 'b'
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.pages
import geb.*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import org.springframework.context.annotation.Bean;
@@ -21,7 +22,6 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@EnableRedisHttpSession
public class Config {
@@ -40,4 +40,4 @@ public class Config {
return serializer;
}
// end::cookie-serializer[]
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,16 +13,16 @@
* 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 class Initializer extends AbstractHttpSessionApplicationInitializer { // <1>
public Initializer() {
super(Config.class); // <2>
}
}
// end::class[]
// end::class[]

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,19 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
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 {
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);
@@ -34,4 +39,4 @@ public class SessionServlet extends HttpServlet {
private static final long serialVersionUID = 2878267318695777395L;
}
// tag::end[]
// tag::end[]

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,11 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<link rel="stylesheet" href="assets/bootstrap.min.css">
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -10,8 +10,7 @@ buildscript {
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
apply from: SAMPLE_GRADLE
group = 'samples'
@@ -21,6 +20,10 @@ dependencies {
"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.webjars:webjars-locator",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"com.maxmind.geoip2:geoip2:2.3.1",
@@ -53,4 +56,4 @@ def reservePort() {
def result = socket.localPort
socket.close()
result
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample
import geb.Browser
@@ -101,4 +102,4 @@ class FindByUsernameTests extends MultiBrowserGebSpec {
then: 'browser a sent to log in'
a.at LoginPage
}
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import geb.*
import spock.lang.*
@@ -97,4 +98,4 @@ abstract class MultiBrowserGebSpec extends Specification {
def cleanupSpec() {
if (isSpecStepwise()) resetBrowsers()
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.pages
import geb.*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.pages
import geb.*

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,16 +13,17 @@
* 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;
import com.maxmind.geoip2.DatabaseReader;
/**
*
* @author Rob Winch
@@ -32,7 +33,9 @@ import com.maxmind.geoip2.DatabaseReader;
public class GeoConfig {
@Bean
public DatabaseReader geoDatabaseReader(@Value("classpath:GeoLite2-City.mmdb") InputStream geoInputStream) throws Exception {
public DatabaseReader geoDatabaseReader(
@Value("classpath:GeoLite2-City.mmdb") InputStream geoInputStream)
throws Exception {
return new DatabaseReader.Builder(geoInputStream).build();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* 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;
@@ -24,5 +25,6 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
*/
// tag::class[]
@EnableRedisHttpSession // <1>
public class HttpSessionConfig { }
// end::class[]
public class HttpSessionConfig {
}
// end::class[]

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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;
@@ -30,24 +31,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
// tag::config[]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.permitAll();
http.formLogin().loginPage("/login").permitAll().and().authorizeRequests()
.antMatchers("/resources/**", "/webjars/**").permitAll().anyRequest().authenticated()
.and().logout().permitAll();
}
// end::config[]
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* 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 java.security.Principal;
@@ -42,22 +43,26 @@ public class IndexController {
@RequestMapping("/")
public String index(Principal principal, Model model) {
Collection<? extends ExpiringSession> usersSessions =
sessions.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName()).values();
Collection<? extends ExpiringSession> usersSessions = this.sessions
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName())
.values();
model.addAttribute("sessions", usersSessions);
return "index";
}
// end::findbyusername[]
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
public String removeSession(Principal principal, @PathVariable String sessionIdToDelete) {
Set<String> usersSessionIds = sessions.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
public String removeSession(Principal principal,
@PathVariable String sessionIdToDelete) {
Set<String> usersSessionIds = this.sessions.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal.getName()).keySet();
if(usersSessionIds.contains(sessionIdToDelete)) {
sessions.delete(sessionIdToDelete);
if (usersSessionIds.contains(sessionIdToDelete)) {
this.sessions.delete(sessionIdToDelete);
}
return "redirect:/";
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.mvc;
import org.springframework.stereotype.Controller;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.session;
import java.io.Serializable;
@@ -30,7 +31,7 @@ public class SessionDetails implements Serializable {
private String accessType;
public String getLocation() {
return location;
return this.location;
}
public void setLocation(String location) {
@@ -38,7 +39,7 @@ public class SessionDetails implements Serializable {
}
public String getAccessType() {
return accessType;
return this.accessType;
}
public void setAccessType(String accessType) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.session;
import java.io.IOException;
@@ -24,20 +25,20 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.CityResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.CityResponse;
/**
* Inserts the session details into the session for every request. Some users
* may prefer to insert session details only after authentication. This is fine,
* but it may be valuable to the most up to date information so that if someone
* stole the user's session id it can be observed.
* Inserts the session details into the session for every request. Some users may prefer
* to insert session details only after authentication. This is fine, but it may be
* valuable to the most up to date information so that if someone stole the user's session
* id it can be observed.
*
* @author Rob Winch
*
@@ -56,8 +57,8 @@ public class SessionDetailsFilter extends OncePerRequestFilter {
}
// tag::dofilterinternal[]
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
HttpSession session = request.getSession(false);
@@ -76,18 +77,21 @@ public class SessionDetailsFilter extends OncePerRequestFilter {
String getGeoLocation(String remoteAddr) {
try {
CityResponse city = reader.city(InetAddress.getByName(remoteAddr));
CityResponse city = this.reader.city(InetAddress.getByName(remoteAddr));
String cityName = city.getCity().getName();
String countryName = city.getCountry().getName();
if(cityName == null && countryName == null) {
if (cityName == null && countryName == null) {
return null;
} else if(cityName == null) {
}
else if (cityName == null) {
return countryName;
} else if(countryName == null) {
}
else if (countryName == null) {
return cityName;
}
return cityName + ", " + countryName;
} catch (Exception e) {
}
catch (Exception e) {
return UNKNOWN;
}
@@ -97,11 +101,11 @@ public class SessionDetailsFilter extends OncePerRequestFilter {
String remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (remoteAddr == null) {
remoteAddr = request.getRemoteAddr();
} else if (remoteAddr.contains(",")) {
}
else if (remoteAddr.contains(",")) {
remoteAddr = remoteAddr.split(",")[0];
}
return remoteAddr;
}
}
//end::class[]
// end::class[]

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
<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="@{/resources/css/bootstrap.css}" href="../static/css/bootstrap.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
@@ -65,11 +65,11 @@
margin-left: 1em;
}
</style>
<link th:href="@{resources/css/bootstrap-responsive.css}" href="/static/css/bootstrap-responsive.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<![endif]-->
</head>
@@ -119,4 +119,4 @@
</div>
</div>
</body>
</html>
</html>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.session;
import static org.assertj.core.api.Assertions.*;
import com.maxmind.geoip2.DatabaseReader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import sample.config.GeoConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.maxmind.geoip2.DatabaseReader;
import sample.config.GeoConfig;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
@@ -42,21 +43,24 @@ public class SessionDetailsFilterTests {
@Before
public void setup() {
filter = new SessionDetailsFilter(reader);
this.filter = new SessionDetailsFilter(this.reader);
}
@Test
public void getGeoLocationHanldesInvalidIp() {
assertThat(filter.getGeoLocation("a")).isEqualTo(SessionDetailsFilter.UNKNOWN);
assertThat(this.filter.getGeoLocation("a"))
.isEqualTo(SessionDetailsFilter.UNKNOWN);
}
@Test
public void getGeoLocationNullCity() {
assertThat(filter.getGeoLocation("22.231.113.64")).isEqualTo("United States");
assertThat(this.filter.getGeoLocation("22.231.113.64"))
.isEqualTo("United States");
}
@Test
public void getGeoLocationBoth() {
assertThat(filter.getGeoLocation("184.154.83.119")).isEqualTo("Chicago, United States");
assertThat(this.filter.getGeoLocation("184.154.83.119"))
.isEqualTo("Chicago, United States");
}
}
}

View File

@@ -0,0 +1,75 @@
buildscript {
ext {
grailsVersion = project.grailsVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0"
classpath "org.grails.plugins:hibernate4:5.0.2"
}
}
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"asset-pipeline"
apply from: SAMPLE_GRADLE
ext {
grailsVersion = project.grailsVersion
gradleWrapperVersion = project.gradleWrapperVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencyManagement {
imports {
mavenBom "org.grails:grails-bom:$grailsVersion"
}
applyMavenExclusions false
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.grails:grails-core"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-dependencies"
compile "org.grails:grails-web-boot"
compile "org.grails.plugins:cache"
compile "org.grails.plugins:scaffolding"
compile "org.grails.plugins:hibernate4"
compile "org.hibernate:hibernate-ehcache"
console "org.grails:grails-console"
profile "org.grails.profiles:web:3.1.4"
runtime "org.grails.plugins:asset-pipeline"
runtime "com.h2database:h2"
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails.plugins:geb"
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
compile "org.springframework.boot:spring-boot-starter-redis"
compile 'org.springframework.session:spring-session:1.1.1.RELEASE'
compile 'org.grails.plugins:spring-security-core:3.0.4'
}
task wrapper(type: Wrapper) {
gradleVersion = gradleWrapperVersion
}
assets {
minifyJs = true
minifyCss = true
}

View File

@@ -0,0 +1,2 @@
grailsVersion=3.1.4
gradleWrapperVersion=2.9

View File

@@ -0,0 +1,28 @@
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'grails3.redis.session.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'grails3.redis.session.UserRole'
grails.plugin.springsecurity.authority.className = 'grails3.redis.session.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
[pattern: '/', access: ['permitAll']],
[pattern: '/error', access: ['permitAll']],
[pattern: '/index', access: ['permitAll']],
[pattern: '/index.gsp', access: ['permitAll']],
[pattern: '/shutdown', access: ['permitAll']],
[pattern: '/assets/**', access: ['permitAll']],
[pattern: '/**/js/**', access: ['permitAll']],
[pattern: '/**/css/**', access: ['permitAll']],
[pattern: '/**/images/**', access: ['permitAll']],
[pattern: '/**/favicon.ico', access: ['permitAll']]
]
grails.plugin.springsecurity.filterChain.chainMap = [
[pattern: '/assets/**', filters: 'none'],
[pattern: '/**/js/**', filters: 'none'],
[pattern: '/**/css/**', filters: 'none'],
[pattern: '/**/images/**', filters: 'none'],
[pattern: '/**/favicon.ico', filters: 'none'],
[pattern: '/**', filters: 'JOINED_FILTERS']
]

View File

@@ -0,0 +1,122 @@
---
hibernate:
cache:
queries: false
use_second_level_cache: true
use_query_cache: false
region.factory_class: 'org.hibernate.cache.ehcache.EhCacheRegionFactory'
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: update
url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
maxActive: 50
minIdle: 5
maxIdle: 25
maxWait: 10000
maxAge: 600000
timeBetweenEvictionRunsMillis: 5000
minEvictableIdleTimeMillis: 60000
validationQuery: SELECT 1
validationQueryTimeout: 3
validationInterval: 15000
testOnBorrow: true
testWhileIdle: true
testOnReturn: false
jdbcInterceptors: ConnectionState
defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
---
---
grails:
profile: web
codegen:
defaultPackage: grails3.redis.session
spring:
transactionManagement:
proxies: false
info:
app:
name: '@info.app.name@'
version: '@info.app.version@'
grailsVersion: '@info.app.grailsVersion@'
spring:
groovy:
template:
check-template-location: false
---
grails:
mime:
disable:
accept:
header:
userAgents:
- Gecko
- WebKit
- Presto
- Trident
types:
all: '*/*'
atom: application/atom+xml
css: text/css
csv: text/csv
form: application/x-www-form-urlencoded
html:
- text/html
- application/xhtml+xml
js: text/javascript
json:
- application/json
- text/json
multipartForm: multipart/form-data
pdf: application/pdf
rss: application/rss+xml
text: text/plain
hal:
- application/hal+json
- application/hal+xml
xml:
- text/xml
- application/xml
urlmapping:
cache:
maxsize: 1000
controllers:
defaultScope: singleton
converters:
encoding: UTF-8
views:
default:
codec: html
gsp:
encoding: UTF-8
htmlcodec: xml
codecs:
expression: html
scriptlets: html
taglib: none
staticparts: none
endpoints:
jmx:
unique-names: true

View File

@@ -0,0 +1,23 @@
import grails.util.BuildSettings
import grails.util.Environment
// See http://logback.qos.ch/manual/groovy.html for details on configuration
appender('STDOUT', ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%level %logger - %msg%n"
}
}
root(ERROR, ['STDOUT'])
def targetDir = BuildSettings.TARGET_DIR
if (Environment.isDevelopmentMode() && targetDir) {
appender("FULL_STACKTRACE", FileAppender) {
file = "${targetDir}/stacktrace.log"
append = true
encoder(PatternLayoutEncoder) {
pattern = "%level %logger - %msg%n"
}
}
logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
}

View File

@@ -0,0 +1,3 @@
// Place your Spring DSL code here
beans = {
}

View File

@@ -0,0 +1,8 @@
package grails3.redis.session
import grails.plugin.springsecurity.annotation.Secured
class TestController {
@Secured('ROLE_ADMIN')
def index() { } // Renders `test/index.gsp`
}

View File

@@ -0,0 +1,16 @@
package grails3.redis.session
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}

View File

@@ -0,0 +1,26 @@
package grails3.redis.session
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@EqualsAndHashCode(includes='authority')
@ToString(includes='authority', includeNames=true, includePackage=false)
class Role implements Serializable {
private static final long serialVersionUID = 1
String authority
Role(String authority) {
this()
this.authority = authority
}
static constraints = {
authority blank: false, unique: true
}
static mapping = {
cache true
}
}

View File

@@ -0,0 +1,55 @@
package grails3.redis.session
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@EqualsAndHashCode(includes='username')
@ToString(includes='username', includeNames=true, includePackage=false)
class User implements Serializable {
private static final long serialVersionUID = 1
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
User(String username, String password) {
this()
this.username = username
this.password = password
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this)*.role
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
static transients = ['springSecurityService']
static constraints = {
password blank: false, password: true
username blank: false, unique: true
}
static mapping = {
password column: '`password`'
}
}

View File

@@ -0,0 +1,103 @@
package grails3.redis.session
import grails.gorm.DetachedCriteria
import groovy.transform.ToString
import org.apache.commons.lang.builder.HashCodeBuilder
@ToString(cache=true, includeNames=true, includePackage=false)
class UserRole implements Serializable {
private static final long serialVersionUID = 1
User user
Role role
UserRole(User u, Role r) {
this()
user = u
role = r
}
@Override
boolean equals(other) {
if (!(other instanceof UserRole)) {
return false
}
other.user?.id == user?.id && other.role?.id == role?.id
}
@Override
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (role) builder.append(role.id)
builder.toHashCode()
}
static UserRole get(long userId, long roleId) {
criteriaFor(userId, roleId).get()
}
static boolean exists(long userId, long roleId) {
criteriaFor(userId, roleId).count()
}
private static DetachedCriteria criteriaFor(long userId, long roleId) {
UserRole.where {
user == User.load(userId) &&
role == Role.load(roleId)
}
}
static UserRole create(User user, Role role, boolean flush = false) {
def instance = new UserRole(user: user, role: role)
instance.save(flush: flush, insert: true)
instance
}
static boolean remove(User u, Role r, boolean flush = false) {
if (u == null || r == null) return false
int rowCount = UserRole.where { user == u && role == r }.deleteAll()
if (flush) { UserRole.withSession { it.flush() } }
rowCount
}
static void removeAll(User u, boolean flush = false) {
if (u == null) return
UserRole.where { user == u }.deleteAll()
if (flush) { UserRole.withSession { it.flush() } }
}
static void removeAll(Role r, boolean flush = false) {
if (r == null) return
UserRole.where { role == r }.deleteAll()
if (flush) { UserRole.withSession { it.flush() } }
}
static constraints = {
role validator: { Role r, UserRole ur ->
if (ur.user == null || ur.user.id == null) return
boolean existing = false
UserRole.withNewSession {
existing = UserRole.exists(ur.user.id, r.id)
}
if (existing) {
return 'userRole.exists'
}
}
}
static mapping = {
id composite: ['user', 'role']
version false
}
}

View File

@@ -0,0 +1,56 @@
default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
default.blank.message=Property [{0}] of class [{1}] cannot be blank
default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
default.null.message=Property [{0}] of class [{1}] cannot be null
default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
default.paginate.prev=Previous
default.paginate.next=Next
default.boolean.true=True
default.boolean.false=False
default.date.format=yyyy-MM-dd HH:mm:ss z
default.number.format=0
default.created.message={0} {1} created
default.updated.message={0} {1} updated
default.deleted.message={0} {1} deleted
default.not.deleted.message={0} {1} could not be deleted
default.not.found.message={0} not found with id {1}
default.optimistic.locking.failure=Another user has updated this {0} while you were editing
default.home.label=Home
default.list.label={0} List
default.add.label=Add {0}
default.new.label=New {0}
default.create.label=Create {0}
default.show.label=Show {0}
default.edit.label=Edit {0}
default.button.create.label=Create
default.button.edit.label=Edit
default.button.update.label=Update
default.button.delete.label=Delete
default.button.delete.confirm.message=Are you sure?
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
typeMismatch.java.net.URL=Property {0} must be a valid URL
typeMismatch.java.net.URI=Property {0} must be a valid URI
typeMismatch.java.util.Date=Property {0} must be a valid Date
typeMismatch.java.lang.Double=Property {0} must be a valid number
typeMismatch.java.lang.Integer=Property {0} must be a valid number
typeMismatch.java.lang.Long=Property {0} must be a valid number
typeMismatch.java.lang.Short=Property {0} must be a valid number
typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
typeMismatch.java.math.BigInteger=Property {0} must be a valid number
typeMismatch=Property {0} is type-mismatched

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