Compare commits

..

17 Commits
1.3.x ... 1.2.x

Author SHA1 Message Date
Spring Operator
60e88e8113 URL Cleanup (#1383)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* [ ] http://www.puppycrawl.com/dtds/configuration_1_3.dtd (404) with 1 occurrences migrated to:
  https://www.puppycrawl.com/dtds/configuration_1_3.dtd ([https](https://www.puppycrawl.com/dtds/configuration_1_3.dtd) result 404).
* [ ] http://www.puppycrawl.com/dtds/suppressions_1_1.dtd (404) with 1 occurrences migrated to:
  https://www.puppycrawl.com/dtds/suppressions_1_1.dtd ([https](https://www.puppycrawl.com/dtds/suppressions_1_1.dtd) result 404).

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://www.springframework.org/schema/beans/spring-beans.xsd with 11 occurrences migrated to:
  https://www.springframework.org/schema/beans/spring-beans.xsd ([https](https://www.springframework.org/schema/beans/spring-beans.xsd) result 200).
* [ ] http://www.springframework.org/schema/context/spring-context.xsd with 10 occurrences migrated to:
  https://www.springframework.org/schema/context/spring-context.xsd ([https](https://www.springframework.org/schema/context/spring-context.xsd) result 200).
* [ ] http://www.springframework.org/schema/gemfire/spring-gemfire.xsd with 4 occurrences migrated to:
  https://www.springframework.org/schema/gemfire/spring-gemfire.xsd ([https](https://www.springframework.org/schema/gemfire/spring-gemfire.xsd) result 200).
* [ ] http://www.springframework.org/schema/jdbc/spring-jdbc.xsd with 1 occurrences migrated to:
  https://www.springframework.org/schema/jdbc/spring-jdbc.xsd ([https](https://www.springframework.org/schema/jdbc/spring-jdbc.xsd) result 200).
* [ ] http://www.springframework.org/schema/util/spring-util-4.1.xsd with 2 occurrences migrated to:
  https://www.springframework.org/schema/util/spring-util-4.1.xsd ([https](https://www.springframework.org/schema/util/spring-util-4.1.xsd) result 200).
* [ ] http://www.springframework.org/schema/util/spring-util.xsd with 4 occurrences migrated to:
  https://www.springframework.org/schema/util/spring-util.xsd ([https](https://www.springframework.org/schema/util/spring-util.xsd) result 200).
* [ ] http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd with 4 occurrences migrated to:
  https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd ([https](https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd) result 302).

# Ignored
These URLs were intentionally ignored.

* http://java.sun.com/xml/ns/javaee with 8 occurrences
* http://www.hazelcast.com/schema/config with 6 occurrences
* http://www.springframework.org/schema/beans with 22 occurrences
* http://www.springframework.org/schema/context with 20 occurrences
* http://www.springframework.org/schema/gemfire with 8 occurrences
* http://www.springframework.org/schema/jdbc with 2 occurrences
* http://www.springframework.org/schema/p with 7 occurrences
* http://www.springframework.org/schema/util with 12 occurrences
* http://www.w3.org/2001/XMLSchema-instance with 18 occurrences
2019-04-01 10:23:44 -05:00
Spring Operator
e1cdaf6385 URL Cleanup (#1389)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# HTTP URLs that Could Not Be Fixed
These URLs were unable to be fixed. Please review them to see if they can be manually resolved.

* [ ] http://www.faqs.org/qa/rfcc-1940.html (200) with 3 occurrences could not be migrated:
   ([https](https://www.faqs.org/qa/rfcc-1940.html) result AnnotatedConnectException).
* [ ] http://www.faqs.org/rfcs/rfc3548.html (200) with 3 occurrences could not be migrated:
   ([https](https://www.faqs.org/rfcs/rfc3548.html) result AnnotatedConnectException).
* [ ] http://www.faqs.org/ (301) with 1 occurrences could not be migrated:
   ([https](https://www.faqs.org/) result AnnotatedConnectException).
* [ ] http://iharder.net/base64 (303) with 2 occurrences could not be migrated:
   ([https](https://iharder.net/base64) result AnnotatedConnectException).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* [ ] http://www.ultraq.net.nz/thymeleaf/layout (302) with 11 occurrences migrated to:
  https://github.com/ultraq/thymeleaf-layout-dialect ([https](https://www.ultraq.net.nz/thymeleaf/layout) result ConnectTimeoutException).
* [ ] http://192.168.1.100:8080/ (AnnotatedConnectException) with 1 occurrences migrated to:
  https://192.168.1.100:8080/ ([https](https://192.168.1.100:8080/) result ConnectTimeoutException).
* [ ] http://www.faqs (UnknownHostException) with 1 occurrences migrated to:
  https://www.faqs ([https](https://www.faqs) result UnknownHostException).

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://creativecommons.org/licenses/by/3.0/ with 1 occurrences migrated to:
  https://creativecommons.org/licenses/by/3.0/ ([https](https://creativecommons.org/licenses/by/3.0/) result 200).
* [ ] http://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html with 1 occurrences migrated to:
  https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html ([https](https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html) result 200).
* [ ] http://docs.hazelcast.org/docs/latest/manual/html-single/index.html with 8 occurrences migrated to:
  https://docs.hazelcast.org/docs/latest/manual/html-single/index.html ([https](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) result 200).
* [ ] http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html with 1 occurrences migrated to:
  https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html ([https](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html) result 200).
* [ ] http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ with 4 occurrences migrated to:
  https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ ([https](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) result 200).
* [ ] http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/ with 6 occurrences migrated to:
  https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/ ([https](https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/) result 200).
* [ ] http://docs.spring.io/spring-data-redis/docs/current/reference/html/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-data-redis/docs/current/reference/html/ ([https](https://docs.spring.io/spring-data-redis/docs/current/reference/html/) result 200).
* [ ] http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/ with 5 occurrences migrated to:
  https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/ ([https](https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/) result 200).
* [ ] http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html with 4 occurrences migrated to:
  https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html ([https](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html) result 200).
* [ ] http://docs.spring.io/spring-session/docs/current/reference/html5/ with 1 occurrences migrated to:
  https://docs.spring.io/spring-session/docs/current/reference/html5/ ([https](https://docs.spring.io/spring-session/docs/current/reference/html5/) result 200).
* [ ] http://getbootstrap.com/getting-started/ with 1 occurrences migrated to:
  https://getbootstrap.com/getting-started/ ([https](https://getbootstrap.com/getting-started/) result 200).
* [ ] http://hazelcast.org/ with 1 occurrences migrated to:
  https://hazelcast.org/ ([https](https://hazelcast.org/) result 200).
* [ ] http://logback.qos.ch/manual/groovy.html with 1 occurrences migrated to:
  https://logback.qos.ch/manual/groovy.html ([https](https://logback.qos.ch/manual/groovy.html) result 200).
* [ ] http://projects.spring.io/spring-session/ with 1 occurrences migrated to:
  https://projects.spring.io/spring-session/ ([https](https://projects.spring.io/spring-session/) result 200).
* [ ] http://redis.io/commands with 1 occurrences migrated to:
  https://redis.io/commands ([https](https://redis.io/commands) result 200).
* [ ] http://redis.io/commands/expire with 1 occurrences migrated to:
  https://redis.io/commands/expire ([https](https://redis.io/commands/expire) result 200).
* [ ] http://redis.io/commands/hmset with 1 occurrences migrated to:
  https://redis.io/commands/hmset ([https](https://redis.io/commands/hmset) result 200).
* [ ] http://redis.io/download with 10 occurrences migrated to:
  https://redis.io/download ([https](https://redis.io/download) result 200).
* [ ] http://redis.io/topics/data-types with 1 occurrences migrated to:
  https://redis.io/topics/data-types ([https](https://redis.io/topics/data-types) result 200).
* [ ] http://redis.io/topics/notifications with 5 occurrences migrated to:
  https://redis.io/topics/notifications ([https](https://redis.io/topics/notifications) result 200).
* [ ] http://redis.io/topics/quickstart with 7 occurrences migrated to:
  https://redis.io/topics/quickstart ([https](https://redis.io/topics/quickstart) result 200).
* [ ] http://stackoverflow.com with 1 occurrences migrated to:
  https://stackoverflow.com ([https](https://stackoverflow.com) result 200).
* [ ] http://stackoverflow.com/questions/tagged/spring-session with 1 occurrences migrated to:
  https://stackoverflow.com/questions/tagged/spring-session ([https](https://stackoverflow.com/questions/tagged/spring-session) result 200).
* [ ] http://stackoverflow.com/tags/spring-session with 1 occurrences migrated to:
  https://stackoverflow.com/tags/spring-session ([https](https://stackoverflow.com/tags/spring-session) result 200).
* [ ] http://www.thymeleaf.org with 12 occurrences migrated to:
  https://www.thymeleaf.org ([https](https://www.thymeleaf.org) result 200).
* [ ] http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd with 6 occurrences migrated to:
  https://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd ([https](https://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd) result 200).
* [ ] http://contributor-covenant.org with 1 occurrences migrated to:
  https://contributor-covenant.org ([https](https://contributor-covenant.org) result 301).
* [ ] http://contributor-covenant.org/version/1/3/0/ with 1 occurrences migrated to:
  https://contributor-covenant.org/version/1/3/0/ ([https](https://contributor-covenant.org/version/1/3/0/) result 301).
* [ ] http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html with 5 occurrences migrated to:
  https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html ([https](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html) result 301).
* [ ] http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ with 4 occurrences migrated to:
  https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ ([https](https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/function_exec/chapter_overview.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/function_exec/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/function_exec/chapter_overview.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html with 4 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/creating_map_indexes.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/creating_map_indexes.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/creating_map_indexes.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/query_index.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/query_index.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/query_index.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html with 5 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/product_intro.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/product_intro.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/product_intro.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html with 2 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html with 5 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html with 4 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/p2p_configuration/chapter_overview.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/p2p_configuration/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/p2p_configuration/chapter_overview.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html with 3 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html) result 302).
* [ ] http://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html with 1 occurrences migrated to:
  https://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html ([https](https://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html) result 302).
* [ ] http://spring.io/spring-security with 4 occurrences migrated to:
  https://spring.io/spring-security ([https](https://spring.io/spring-security) result 302).
* [ ] http://www.maxmind.com with 3 occurrences migrated to:
  https://www.maxmind.com ([https](https://www.maxmind.com) result 302).

# Ignored
These URLs were intentionally ignored.

* http://java.sun.com/jsp/jstl/core with 14 occurrences
* http://localhost:8080/ with 45 occurrences
* http://localhost:8080/?_s=0 with 1 occurrences
* http://localhost:8080/?_s=1 with 2 occurrences
* http://localhost:8080/h2-console/ with 3 occurrences
* http://localhost:8080/logout with 1 occurrences
* http://localhost:8080/test/index with 2 occurrences
* http://localhost:xxxxx/hazelcast/rest/maps/spring:session:sessions/7e8383a4-082c-4ffe-a4bc-c40fd3363c5e with 1 occurrences
* http://www.w3.org/1999/xhtml with 6 occurrences
2019-04-01 10:22:49 -05:00
Spring Operator
0c93235dc3 URL Cleanup
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* http://www.apache.org/licenses/LICENSE-2.0 with 287 occurrences migrated to:
  https://www.apache.org/licenses/LICENSE-2.0 ([https](https://www.apache.org/licenses/LICENSE-2.0) result 200).
* http://www.apache.org/licenses/LICENSE-2.0.html with 2 occurrences migrated to:
  https://www.apache.org/licenses/LICENSE-2.0.html ([https](https://www.apache.org/licenses/LICENSE-2.0.html) result 200).
2019-03-14 20:37:55 -05:00
Rob Winch
ab208e2bbb URL Cleanup
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortner).

# HTTP URLs that Could Not Be Fixed
These URLs were unable to be fixed. Please review them to see if they can be manually resolved.

* http://aopalliance.sourceforge.net/doc/ (200) migrated to:
  http://aopalliance.sourceforge.net/doc/ ([https](https://aopalliance.sourceforge.net/doc/) result AnnotatedConnectException).
* http://dist.gemstone.com/maven/release (404) migrated to:
  http://dist.gemstone.com/maven/release ([https](https://dist.gemstone.com/maven/release) result SSLHandshakeException).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* http://www.quartz-scheduler.org/api/2.2.0/ (404) migrated to:
  https://www.quartz-scheduler.org/api/2.2.0/ ([https](https://www.quartz-scheduler.org/api/2.2.0/) result 404).

## Fixed Success
These URLs were fixed successfully.

* http://commons.apache.org/proper/commons-codec/apidocs/ migrated to:
  https://commons.apache.org/proper/commons-codec/apidocs/ ([https](https://commons.apache.org/proper/commons-codec/apidocs/) result 200).
* http://commons.apache.org/proper/commons-dbcp/apidocs/ migrated to:
  https://commons.apache.org/proper/commons-dbcp/apidocs/ ([https](https://commons.apache.org/proper/commons-dbcp/apidocs/) result 200).
* http://commons.apache.org/proper/commons-lang/javadocs/api-2.5/ migrated to:
  https://commons.apache.org/proper/commons-lang/javadocs/api-2.5/ ([https](https://commons.apache.org/proper/commons-lang/javadocs/api-2.5/) result 200).
* http://docs.jboss.org/jbossas/javadoc/4.0.5/connector/ migrated to:
  https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/ ([https](https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/) result 200).
* http://docs.jboss.org/jbossas/javadoc/7.1.2.Final/ migrated to:
  https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/ ([https](https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/) result 200).
* http://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/ migrated to:
  https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/ ([https](https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/) result 200).
* http://docs.oracle.com/javaee/7/api/ migrated to:
  https://docs.oracle.com/javaee/7/api/ ([https](https://docs.oracle.com/javaee/7/api/) result 200).
* http://docs.oracle.com/javase/8/docs/api/ migrated to:
  https://docs.oracle.com/javase/8/docs/api/ ([https](https://docs.oracle.com/javase/8/docs/api/) result 200).
* http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/ migrated to:
  https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/ ([https](https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/) result 200).
* http://portals.apache.org/pluto/portlet-2.0-apidocs/ migrated to:
  https://portals.apache.org/pluto/portlet-2.0-apidocs/ ([https](https://portals.apache.org/pluto/portlet-2.0-apidocs/) result 200).
* http://tiles.apache.org/framework/apidocs/ migrated to:
  https://tiles.apache.org/framework/apidocs/ ([https](https://tiles.apache.org/framework/apidocs/) result 200).
* http://tiles.apache.org/tiles-request/apidocs/ migrated to:
  https://tiles.apache.org/tiles-request/apidocs/ ([https](https://tiles.apache.org/tiles-request/apidocs/) result 200).
* http://www.apache.org/licenses/LICENSE-2.0.txt migrated to:
  https://www.apache.org/licenses/LICENSE-2.0.txt ([https](https://www.apache.org/licenses/LICENSE-2.0.txt) result 200).
* http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/ migrated to:
  https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/ ([https](https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/) result 200).
* http://fasterxml.github.com/jackson-core/javadoc/2.3.0/ migrated to:
  https://fasterxml.github.com/jackson-core/javadoc/2.3.0/ ([https](https://fasterxml.github.com/jackson-core/javadoc/2.3.0/) result 301).
* http://fasterxml.github.com/jackson-databind/javadoc/2.3.0/ migrated to:
  https://fasterxml.github.com/jackson-databind/javadoc/2.3.0/ ([https](https://fasterxml.github.com/jackson-databind/javadoc/2.3.0/) result 301).
* http://glassfish.java.net/nonav/docs/v3/api/ migrated to:
  https://glassfish.java.net/nonav/docs/v3/api/ ([https](https://glassfish.java.net/nonav/docs/v3/api/) result 301).
* http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/ migrated to:
  https://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/ ([https](https://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/) result 301).
* http://projects.spring.io/spring-session migrated to:
  https://projects.spring.io/spring-session ([https](https://projects.spring.io/spring-session) result 301).
2019-03-05 08:30:48 -06:00
Spring Buildmaster
b760b1886d Next development version 2016-09-07 17:27:46 +00:00
Spring Buildmaster
5d42040524 Release version 1.2.2.RELEASE 2016-09-07 17:27:40 +00:00
Rob Winch
7c66afe2e1 Polish JDBC Bean ClassLoader
Issue gh-610
2016-09-07 11:08:14 -05:00
Vedran Pavic
62b5efe838 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:06:46 -05:00
Rob Winch
b79dc79dbc Add ability to set delimiters for CookieHttpSessionStrategy
Fixes gh-615
2016-09-06 23:21:51 -05:00
Rob Winch
1ead9f744c Polish Base64 DefaultCookieSerializer Support
Issue gh-611
2016-09-06 23:21:49 -05:00
Vedran Pavic
7fd0739c20 Add DefaultCookieSerializer Base64 Support
Fixes gh-611
2016-09-06 23:21:47 -05:00
Rob Winch
352c234d21 Remove only master from .travis.yml 2016-08-30 09:16:54 -05:00
John Blum
617bd340d0 Register non-anonymous, named Instantiators for GemFireSession and GemFireSessionAttributes (#594)
Fixes gh-594 & gh-595
2016-08-17 17:25:02 -07:00
Rob Winch
70fc4c1ede Optimize save operation in JdbcOperationsSessionRepository (#582) (#601)
This commit improves saving of new sessions to only execute batch update operation if there really are any attributes to save.
2016-08-17 10:05:01 -05:00
Rob Winch
e44b99ac14 Improve result set extraction in JdbcOperationsSessionRepository (#577) (#600)
Improve result set extraction in JdbcOperationsSessionRepository
2016-08-17 09:27:09 -05:00
John Blum
f6101b7e30 Fix deserialization issue caused by unregistered Instantiator (#595)
Fixes gh-594 & gh-595
2016-08-16 21:09:01 -05:00
John Blum
240c979679 Set version to 1.2.2.BUILD-SNAPSHOT. 2016-08-16 15:47:46 -07:00
231 changed files with 2681 additions and 16081 deletions

4
.gitignore vendored
View File

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

View File

@@ -3,8 +3,7 @@ buildscript {
maven { url "https://repo.spring.io/plugins-release" }
}
dependencies {
classpath 'io.spring.gradle:dependency-management-plugin:0.6.1.RELEASE'
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.5")
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("io.spring.gradle:spring-io-plugin:0.0.4.RELEASE")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
@@ -14,17 +13,16 @@ buildscript {
}
plugins {
id "org.sonarqube" version "2.1"
id "org.sonarqube" version "1.2"
}
group = 'org.springframework.session'
ext.springBootVersion = '1.4.7.RELEASE'
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.BOM_GRADLE = "$rootDir/gradle/bom.gradle"
ext.SAMPLE_GRADLE = "$rootDir/gradle/sample.gradle"
ext.TOMCAT_GRADLE = "$rootDir/gradle/tomcat.gradle"
ext.TOMCAT_6_GRADLE = "$rootDir/gradle/tomcat6.gradle"
@@ -51,14 +49,12 @@ sonarqube {
}
}
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) {
doLast {
project.tasks.docsZip.from(project(':docs').asciidoctor) {
into('reference')
}
project.tasks.docsZip.from(project(':spring-session').javadoc) {
into('api')
}
task configDocsZip(dependsOn: [':docs:asciidoctor',':spring-session:javadoc']) << {
project.tasks.docsZip.from(project(':docs').asciidoctor) {
into('reference')
}
project.tasks.docsZip.from(project(':spring-session').javadoc) {
into('api')
}
}
@@ -73,4 +69,4 @@ task docsZip(type: Zip, dependsOn: 'configDocsZip') {
artifacts {
archives docsZip
}
}

View File

@@ -75,7 +75,7 @@
<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.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo"/>
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"/>

View File

@@ -32,19 +32,17 @@ dependencies {
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
"org.springframework:spring-webmvc:${springVersion}",
"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:$junitVersion",
"org.mockito:mockito-core:$mockitoVersion",
'junit:junit:4.11',
'org.mockito:mockito-core:1.9.5',
"org.springframework:spring-test:$springVersion",
"org.assertj:assertj-core:$assertjVersion",
"com.hazelcast:hazelcast:$hazelcastVersion",
"biz.paluch.redis:lettuce:$lettuceVersion",
"redis.clients:jedis:2.4.1",
"javax.servlet:javax.servlet-api:$servletApiVersion"
}
@@ -59,7 +57,6 @@ asciidoctor {
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
'spring-session-version' : version,
'spring-version' : springVersion,
'lettuce-version' : lettuceVersion,
'hazelcast-version' : hazelcastVersion,
'docs-itest-dir' : rootProject.projectDir.path + '/docs/src/integration-test/java/',
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',

View File

@@ -25,7 +25,7 @@ If you are using Maven, ensure to add the following dependencies:
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
</dependencies>
----

View File

@@ -79,7 +79,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -6,6 +6,8 @@ This guide describes how to use Spring Session to find sessions by username.
NOTE: The completed guide can be found in the <<findbyusername-sample, findbyusername application>>.
NOTE: This feature will likely be refactored in the next release to account for https://github.com/spring-projects/spring-session/issues/301[#301]
[[findbyusername-assumptions]]
== Assumptions
@@ -31,10 +33,10 @@ Consider the following scenario:
Wouldn't it be nice if we could allow the user to invalidate the session at the library from any device they authenticate with?
This sample demonstrates how this is possible.
[[findbyindexnamesessionrepository]]
[[findbyprincipalnamesessionrepository]]
== FindByIndexNameSessionRepository
In order to look up a user by their username, you must first choose a `SessionRepository` that implements link:../#api-findbyindexnamesessionrepository[FindByIndexNameSessionRepository].
In order to look up a user by their username, you must first choose a `SessionRepository` that implements <<index.doc#api-findbyprincipalnamesessionrepository,FindByIndexNameSessionRepository>>.
Our sample application assumes that the Redis support is already setup, so we are ready to go.
== Mapping the username

View File

@@ -127,6 +127,3 @@ Alternatively, you can also delete the explicit key. Enter the following into yo
$ 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.
NOTE: Spring Session will not work with grails flash scope without additional work. +
See this answer for an explanation: https://stackoverflow.com/a/43311427

View File

@@ -64,8 +64,6 @@ Ensure you have the following in your pom.xml:
----
endif::[]
// tag::config[]
[[security-spring-configuration]]
== Spring Configuration
@@ -81,9 +79,7 @@ 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> 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.
<2> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
By default, an embedded instance of Hazelcast is started and connected to by the application.
For more information on configuring Hazelcast, refer to the https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
@@ -92,8 +88,8 @@ For more information on configuring Hazelcast, refer to the https://docs.hazelca
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 `SessionConfig` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our `SessionConfig` class to it.
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our Config class to it.
.src/main/java/sample/SecurityInitializer.java
[source,java]
@@ -117,7 +113,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
@@ -192,4 +188,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

@@ -89,17 +89,16 @@ We start with the _Spring Boot_ application for configuring and bootstrapping a
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 to mainly define the corresponding
<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` that allow us to configure certain aspects of the GemFire Server using
https://gemfire.docs.pivotal.io/docs-gemfire/latest/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 configure and start a `CacheServer` running in the GemFire Server to listen for connections
from cache clients. The `CacheServer's` `Socket` will be used to connect our GemFire cache client,
_Spring Boot_ web application to the server.
<2> Next, we define a few `Properties` allowing us to configure certain aspects of the GemFire Server using
https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
<3> Then, we create an instance of the GemFire Cache using our defined `Properties`.
<4> Finally, we setup a Cache Server instance running in the GemFire Server to listen for connections from cache clients.
The Cache Server `Socket` will be used to connect our GemFire client cache, _Spring Boot_ web application to the server.
The sample also makes use of a `PropertySourcesPlaceholderConfigurer` bean in order to externalize the sample application
configuration to affect GemFire and application configuration/behavior from the command-line (e.g. such as GemFire's
@@ -107,9 +106,9 @@ configuration to affect GemFire and application configuration/behavior from the
=== Spring Boot-based GemFire cache client Web application
Now, we create our _Spring Boot_ Web application exposing our Web service with Spring MVC, running as a
GemFire cache client connected to our _Spring Boot_-based GemFire Server, using Spring Session backed by GemFire
to manage Session state in a clustered, replicated fashion.
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]
----
@@ -117,29 +116,28 @@ include::{samples-dir}httpsession-gemfire-boot/src/main/java/sample/client/Appli
----
<1> Here, again, we use the `@EnableGemFireHttpSession` annotation to not only configure the GemFire cache client,
but to also override the (HTTP) Web application container's `HttpSession` and replace it with a Session implementation
backed by _Spring Session_ and 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,
on the server, which will appropriately notify the cache client when the Session times out. Again, we have just
resorted to using the default named 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> Similarly 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 a
pool of connections to the server. Whenever a client Region entry operation corresponding to a Session update occurs,
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 are not required by any production code. Specifically, the `BeanPostProcessor` along with the code referenced in *6*
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> Navigates the Web application to the home page (`index.html`), which uses **Thymeleaf** templates for server-side
<6> See <5> above.
<7> Navigates the Web application to the home page (`index.html`), which uses **Thymeleaf** templates for server-side
pages.
<7> Heartbeat Web service endpoint (useful for manual testing purposes).
<8> Web service endpoint allowing the user to add a Session attribute using the Web application UI. In addition,
<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".
@@ -159,15 +157,13 @@ and GemFire out-of-the-box using the following attributes:
* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*").
* `clientRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
with a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
(default is `PROXY`). This attribute is only used when configuring client Region.
* `poolName` - name of the dedicated GemFire Pool used to connect a client to the cluster of servers. The attribute
is only used when the application is a GemFire cache client. Defaults to `gemfirePool`.
* `serverRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
using a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
(default is `PARTITION`). This attribute is only used when configuring server Regions, or when a p2p topology is employed.
NOTE: It is important to remember that the GemFire client Region name must match a server Region by the same name if
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
@@ -230,10 +226,7 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see https://gemfire.docs.pivotal.io/docs-gemfire/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
NOTE: In order to run the following, you must uncomment the lines in the `GemFireServer` class, `gemfireProperties` bean
for the following GemFire System properties: `jmx-manager` and `jmx-manager-start`.
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -83,23 +83,23 @@ Add the following Spring Configuration:
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/spring/session-client.xml[tags=beans]
----
<1> Spring annotation configuration support is enabled with `<context:annotation-config/>` element so that any
<1> First, a `Properties` bean is created to reference GemFire configuration common to both the client and server,
stored in the `META-INF/spring/application.properties` file.
<2> The `application.properties` are used along with the `PropertySourcesPlaceholderConfigurer` bean to replace
placeholders in the Spring XML configuration meta-data with property values.
<3> Spring annotation configuration support is enabled with `<context:annotation-config/>` element so that any
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
by Spring will be configured appropriately.
<2> The `META-INF/spring/application.properties` file are used along with the `PropertySourcesPlaceholderConfigurer`
bean to replace placeholders in the Spring XML configuration meta-data with the approrpriate property values.
<3> Then the `GemFireCacheSeverReadyBeanPostProcessor`is registered to determine whether a GemFire Server
at the designated host/port is running and listening for client connections, blocking client startup until
the server is available and ready.
<4> Next, we include a `Properties` bean to configure certain aspects of the GemFire client cache using
https://gemfire.docs.pivotal.io/docs-gemfire/latest/reference/topics/gemfire_properties.html[GemFire's System Properties].
In this case, we are just setting GemFire's `log-level` from a application-specific System property, defaulting
<4> `GemFireHttpSessionConfiguration` is registered to enable Spring Session functionality.
<5> Then, a Spring `BeanPostProcessor` is registered to determine whether a GemFire Server at the designated host/port
is running, blocking client startup until the server is available.
<6> Next, we include a `Properties` bean to configure certain aspects of the GemFire client cache using
https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
In this case, we are just setting GemFire's `log-level` from a sample application-specific System property, defaulting
to `warning` if unspecified.
<5> Then we create a instance of a GemFire `ClientCache` initialized with our `gemfireProperties`.
<6> We configure a Pool of client connections to talk to the GemFire Server in our Client/Server topology.
In our configuration, we use sensible settings for timeouts, number of connections and so on. Also, our `Pool`
has been configured to connect directly to a server.
<7> Finally, the `GemFireHttpSessionConfiguration` is registered to enable Spring Session functionality.
<7> Finally, we create the GemFire client cache and configure a Pool of client connections to talk to the GemFire Server
in our Client/Server topology. In our configuration, we use sensible settings for timeouts, number of connections
and so on. Also, our `Pool` has been configured to connect directly to a server.
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers),
it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data
@@ -110,8 +110,8 @@ NOTE: For more information on configuring _Spring Data GemFire_, refer to the ht
=== Server Configuration
We have only covered one side of the equation. We also need a GemFire Server for our client to talk to and send
session state information to the server to manage.
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
session state information up to the server to manage.
In this sample, we will use the following GemFire Server Java Configuration:
@@ -124,16 +124,15 @@ include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/resources/ME
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
by Spring will be configured appropriately.
<2> A `PropertySourcesPlaceholderConfigurer` is registered to replace placeholders in our Spring XML configuration
meta-data with property values in the `META-INF/spring/application.properties` file.
<3> Next, we configure the GemFire Server using GemFire System Properties very much like our P2P samples.
meta-data with property values from `META-INF/spring/application.properties`.
<3> We enable the same Spring Session functionality that we used on the client by registering an instance of `GemFireHttpSessionConfiguration`,
except that we set the session expiration timeout to **30 seconds**. We will explain later what this means.
<4> Next, we configure the GemFire Server using GemFire System properties very much like our P2P samples.
With the `mcast-port` set to 0 and no `locators` property specified, our server will be standalone. We also allow a
JMX client (e.g. _Gfsh_) to connect to our server with the use of the GemFire-specific JMX System properties.
<4> Then we create an instance of a GemFire peer `Cache` initialized with our GemFire System Properties.
<5> We also setup a GemFire `CacheServer` instance running on *localhost*, listening to port **11235**,
ready to accept our client connection.
<6> Finally, we enable the same Spring Session functionality we used on the client by registering an instance of
`GemFireHttpSessionConfiguration`, except that we set the session expiration timeout to **30 seconds**.
We will explain later what this means.
<5> Then, we create an instance of the GemFire peer cache using our GemFire System properties.
<6> And finally, we also setup a GemFire `CacheServer` instance running on *localhost*, listening on port **11235**,
to accept our client connection.
The GemFire Server configuration gets bootstrapped with the following:
@@ -234,7 +233,7 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see https://gemfire.docs.pivotal.io/gemfire/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -84,26 +84,33 @@ include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/Clie
----
<1> The `@EnableGemFireHttpSession` annotation creates a Spring bean named `springSessionRepositoryFilter` that
implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session
and GemFire.
implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session.
In this instance, Spring Session is backed by GemFire.
<2> Next, we register a `Properties` bean that allows us to configure certain aspects of the GemFire client cache
using https://gemfire.docs.pivotal.io/docs-gemfire/latest/reference/topics/gemfire_properties.html[GemFire's System properties].
<3> We use the `Properties` to configure an instance of a GemFire `ClientCache`.
<4> Then, we configure a `Pool` of client connections to talk to the GemFire Server in our Client/Server topology. In our
using https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties].
<3> Then, we configure a `Pool` of client connections to talk to the GemFire Server in our Client/Server topology. In our
configuration, we have used sensible settings for timeouts, number of connections and so on. Also, the `Pool` has been
configured to connect directly to a server. Learn more about various `Pool` configuration settings from the
https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html[PoolFactory API].
<56> Finally, we include a Spring `BeanPostProcessor` to block the client until our GemFire Server is up and running,
https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/PoolFactory.html[PoolFactory API].
<4> After configuring a `Pool`, we create an instance of the GemFire client cache using the GemFire `Properties`
and `Pool` to communicate with the server and perform cache data access operations.
<5> Finally, we include a Spring `BeanPostProcessor` to block the client until our GemFire Server is up and running,
listening for and accepting client connections.
The `gemfireCacheServerReadyBeanPostProcessor` is necessary in order to coordinate the client and server in
an automated fashion during testing, but unnecessary in situations where the GemFire cluster is already presently
running, such as in production.
running, such as in production. This `BeanPostProcessor` implements 2 approaches to ensure our server has adequate
time to startup.
The `BeanPostProcessor` uses a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html[ClientMembershipListener]
The first approach uses a timed wait, checking at periodic intervals to determine whether a client `Socket` connection
can be made to the server's `CacheServer` endpoint.
The second approach uses a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html[ClientMembershipListener]
that will be notified when the client has successfully connected to the server. Once a connection has been established,
the listener releases the latch that the `BeanPostProcessor` will wait on (up to the specified timeout) in the
`postProcessAfterInitialization` callback to block the client.
`postProcessAfterInitialization` callback to block the client. Either one of these approaches are sufficient
by themselves, but both are demonstrated here to illustrate how this might work and to give you ideas, or other options
in practice.
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers),
it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data
@@ -115,16 +122,11 @@ NOTE: For more information on configuring _Spring Data GemFire_, refer to the ht
The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both Spring Session
and GemFire out-of-the-box using the following attributes:
* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*").
* `clientRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
with a GemFire https://data-docs-samples.cfapps.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.
* `poolName` - name of the dedicated GemFire Pool used to connect a client to the cluster of servers. The attribute
is only used when the application is a GemFire cache client. Defaults to `gemfirePool`.
* `serverRegionShort` - specifies GemFire's https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
using a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
(default is `PARTITION`). This attribute is only used when configuring server Regions, or when a p2p topology is employed.
* `maxInactiveIntervalInSeconds` - controls HttpSession idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "_ClusteredSpringSessions_").
* `clientRegionShort` - specifies GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies]
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
(default is `PROXY`).
NOTE: It is important to note that the GemFire client Region name must match a server Region by the same name if
the client Region is a `PROXY` or `CACHING_PROXY`. Names are not required to match if the client Region used to
@@ -132,13 +134,13 @@ store Spring Sessions is `LOCAL`, however, keep in mind that your session state
and you lose all benefits of using GemFire to store and manage distributed, replicated session state information
in a cluster.
NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when
a peer-to-peer (P2P) topology, and more specifically, a GemFire peer cache is used.
NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when a peer-to-peer (P2P) topology,
and more specifically, a GemFire peer cache is used.
=== Server Configuration
We have only covered one side of the equation. We also need a GemFire Server for our client to talk to and send
session state to the server to manage.
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
session state up to the server to manage.
In this sample, we will use the following GemFire Server Java Configuration:
@@ -147,15 +149,15 @@ In this sample, we will use the following GemFire Server Java Configuration:
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/ServerConfig.java[tags=class]
----
<1> On the server, we also configure Spring Session using the `@EnableGemFireHttpSession` annotation. This ensures
the Region names on both the client and server match (in this sample, we use the default "_ClusteredSpringSessions_").
<1> On the server, we also configure Spring Session using the `@EnableGemFireHttpSession` annotation. For one, this
ensures that the Region names on both the client and server match (in this sample, we use the default "_ClusteredSpringSessions_").
We have also set the session timeout to **30 seconds**. Later, we will see how this timeout is used.
<2> Next, we configure the GemFire Server using GemFire System Properties very much like our P2P samples.
<2> Next, we configure the GemFire Server using GemFire System properties very much like our P2P samples.
With the `mcast-port` set to 0 and no `locators` property specified, our server will be standalone. We also allow a
JMX client (e.g. _Gfsh_) to connect to our server with the use of the GemFire-specific JMX System properties.
<3> Then, we create an instance of a GemFire peer `Cache` initialized with our GemFire System Properties.
<4> We also setup a GemFire `CacheServer` instance running on **localhost**, listening to port **12480**,
ready to accept our client connection.
<3> Then, we create an instance of the GemFire peer cache using our GemFire System properties.
<4> We also setup a GemFire `CacheServer` instance running on **localhost**, listening on port **12480**,
to accept our client connection.
<5> Finally, we declare a `main` method as an entry point for launching and running our GemFire Server
from the command-line.
@@ -233,7 +235,7 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see https://gemfire.docs.pivotal.io/gemfire/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -176,7 +176,7 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see https://gemfire.docs.pivotal.io/gemfire/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -104,7 +104,7 @@ and GemFire out-of-the-box using the following attributes:
* `maxInactiveIntervalInSeconds` - controls HttpSession idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "_ClusteredSpringSessions_").
* `serverRegionShort` - specifies GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies]
with a GemFire https://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
with a GemFire https://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
(default is `PARTITION`).
NOTE: `clientRegionShort` is ignored in a peer cache configuration and only applies when a client-server topology,
@@ -175,7 +175,7 @@ Go ahead and view the cookies (click for help with https://developer.chrome.com/
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see https://gemfire.docs.pivotal.io/gemfire/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
see https://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

View File

@@ -23,11 +23,6 @@ If you are using Maven, ensure to add the following dependencies:
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@@ -134,7 +129,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -23,11 +23,6 @@ If you are using Maven, ensure to add the following dependencies:
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@@ -126,7 +121,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -23,11 +23,6 @@ If you are using Maven, ensure to add the following dependencies:
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@@ -127,7 +122,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -19,20 +19,15 @@ If you are using Maven, ensure to add the following dependencies:
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>{lettuce-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
</dependencies>
----
@@ -131,7 +126,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -20,7 +20,7 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -85,7 +85,7 @@ include::{samples-dir}websocket/src/main/java/sample/config/WebSecurityConfig.ja
[NOTE]
====
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
Alternatively, you can update the `LettuceConnectionFactory` to point to a Redis server.
Alternatively, you can update the `JedisConnectionFactory` to point to a Redis server.
====
----

View File

@@ -1,4 +1,3 @@
= Spring Session
Rob Winch, Vedran Pavić, Jakub Kubrynski
:doctype: book
@@ -23,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.3
== What's New in 1.2
Below are the highlights of what is new in Spring Session 1.3. You can find a complete list of what's new by referring to the changelogs of
https://github.com/spring-projects/spring-session/milestone/6?closed=1[1.3.0.M1],
https://github.com/spring-projects/spring-session/milestone/18?closed=1[1.3.0.M2],
https://github.com/spring-projects/spring-session/milestone/16?closed=1[1.3.0.RC1], and
https://github.com/spring-projects/spring-session/milestone/19?closed=1[1.3.0.RELEASE].
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.
* First class support for https://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#httpsession-hazelcast[Hazelcast]
* First class support for https://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#spring-security-concurrent-sessions-how[Spring Security's concurrent session management]
* Added https://github.com/maseev/spring-session-orientdb[OrientDB Community Extension]
* https://github.com/spring-projects/spring-session/tree/1.3.0.RELEASE/samples/httpsession-redis-json[GenericJackson2JsonRedisSerializer sample] with Spring Security's new Jackson Support
* Guides now https://github.com/spring-projects/spring-session/pull/652[use Lettuce]
* `spring.session.cleanup.cron.expression` can be used to override the cleanup tasks cron expression
* Lots of performance improvements and bug fixes
* 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)
@@ -192,7 +185,7 @@ The two most common topologies to manage Spring Sessions using GemFire include:
* <<httpsession-gemfire-clientserver,Client-Server>>
* <<httpsession-gemfire-p2p,Peer-To-Peer (P2P)>>
Additionally, GemFire supports site-to-site replication using https://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN functionality].
Additionally, GemFire supports site-to-site replication using https://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN functionality].
The ability to configure and use GemFire's WAN support is independent of Spring Session, and is beyond the scope
of this document. More details on GemFire WAN functionality can be found https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:gateway[here].
@@ -375,18 +368,6 @@ There is also a constructor taking `Serializer` and `Deserializer` objects, allo
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
@@ -524,75 +505,6 @@ Before using WebSocket integration, you should be sure that you have <<httpsessi
include::guides/websocket.adoc[tags=config,leveloffset=+2]
[[spring-security]]
== Spring Security Integration
Spring Session provides integration with Spring Security.
[[spring-security-rememberme]]
=== Spring Security Remember-Me Support
Spring Session provides integration with https://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/#remember-me[Spring Security's Remember-Me Authentication].
The support will:
* Change the session expiration length
* Ensure the session cookie expires at `Integer.MAX_VALUE`.
The cookie expiration is set to the largest possible value because the cookie is only set when the session is created.
If it were set to the same value as the session expiration, then the session would get renewed when the user used it but the cookie expiration would not be updated causing the expiration to be fixed.
To configure Spring Session with Spring Security in Java Configuration use the following as a guide:
[source,java,indent=0]
----
include::{docs-test-dir}docs/security/RememberMeSecurityConfiguration.java[tags=http-rememberme]
}
include::{docs-test-dir}docs/security/RememberMeSecurityConfiguration.java[tags=rememberme-bean]
----
An XML based configuration would look something like this:
[source,xml,indent=0]
----
include::{docs-test-resources-dir}docs/security/RememberMeSecurityConfigurationXmlTests-context.xml[tags=config]
----
[[spring-security-concurrent-sessions]]
=== Spring Security Concurrent Session Control
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.
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
@@ -698,6 +610,44 @@ 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 https://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 https://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
@@ -829,12 +779,12 @@ EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
When a session expires key is deleted or expires, the keyspace notification triggers a lookup of the actual session and a SessionDestroyedEvent is fired.
One problem with relying on Redis expiration exclusively is that Redis makes no guarantee of when the expired event will be fired if the key has not been accessed.
One problem with relying on Redis expiration exclusively is that Redis makes no guarantee of when the expired event will be fired if they key has not been accessed.
Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
For additional details see https://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access the key.
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access they key.
For this reason, each session expiration is also tracked to the nearest minute.
This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
@@ -846,7 +796,7 @@ EXPIRE spring:session:expirations1439245080000 2100
----
The background task will then use these mappings to explicitly request each key.
By accessing the key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
[NOTE]
====
@@ -887,7 +837,7 @@ For example, Java Configuration can use the following:
include::{docs-test-dir}docs/RedisHttpSessionConfigurationNoOpConfigureRedisActionTests.java[tags=configure-redis-action]
----
XML Configuration can use the following:
XML Configuraiton can use the following:
[source,xml,indent=0]
----
@@ -1093,7 +1043,6 @@ However, you can override the default `ConversionService` by providing a Bean na
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.
@@ -1117,54 +1066,6 @@ include::{session-main-resources-dir}org/springframework/session/jdbc/schema-mys
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 https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
[[api-enablehazelcasthttpsession]]
==== EnableHazelcastHttpSession
If you wish to use https://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
@@ -1197,20 +1098,6 @@ We appreciate https://help.github.com/articles/using-pull-requests/[Pull Request
Spring Session is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
[[community-extensions]]
=== Community Extensions
|===
| Name | Location
| Spring Session OrientDB
| https://github.com/maseev/spring-session-orientdb
| Spring Session Infinispan
| https://infinispan.org/docs/dev/user_guide/user_guide.html#externalizing_session_using_spring_session
|===
[[minimum-requirements]]
== Minimum Requirements

View File

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

View File

@@ -16,23 +16,17 @@
package docs;
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.lettuce.LettuceConnectionFactory;
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;
@@ -110,7 +104,7 @@ public class IndexDocTests {
@SuppressWarnings("unused")
public void newRedisOperationsSessionRepository() {
// tag::new-redisoperationssessionrepository[]
LettuceConnectionFactory factory = new LettuceConnectionFactory();
JedisConnectionFactory factory = new JedisConnectionFactory();
SessionRepository<? extends ExpiringSession> repository = new RedisOperationsSessionRepository(
factory);
// end::new-redisoperationssessionrepository[]
@@ -141,25 +135,6 @@ public class IndexDocTests {
// 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();

View File

@@ -17,37 +17,21 @@
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;
//tag::config[]
@EnableHazelcastHttpSession // <1>
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
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>
public HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new Config();
return Hazelcast.newHazelcastInstance(hazelcastConfig); // <2>
}
}
// end::config[]

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
/**
* @author rwinch
*/
@EnableWebSecurity
@EnableSpringHttpSession
public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapter {
// @formatter:off
// tag::http-rememberme[]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe()
.rememberMeServices(rememberMeServices());
// end::http-rememberme[]
http
.formLogin().and()
.authorizeRequests()
.anyRequest().authenticated();
}
// tag::rememberme-bean[]
@Bean
public SpringSessionRememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
// end::rememberme-bean[]
// @formatter:on
@Override
@Bean
public InMemoryUserDetailsManager userDetailsService() {
InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager();
uds.createUser(
User.withUsername("user").password("password").roles("USER").build());
return uds;
}
@Bean
MapSessionRepository sessionRepository() {
return new MapSessionRepository();
}
}
// end::class[]

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* @author rwinch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
@WebAppConfiguration
@SuppressWarnings("rawtypes")
public class RememberMeSecurityConfigurationTests<T extends ExpiringSession> {
@Autowired
WebApplicationContext context;
@Autowired
SessionRepositoryFilter springSessionRepositoryFilter;
@Autowired
SessionRepository<T> sessions;
MockMvc mockMvc;
@Before
public void setup() {
// @formatter:off
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.context)
.addFilters(this.springSessionRepositoryFilter)
.apply(springSecurity())
.build();
// @formatter:on
}
@Test
public void authenticateWhenSpringSessionRememberMeEnabledThenCookieMaxAgeAndSessionExpirationSet()
throws Exception {
// @formatter:off
MvcResult result = this.mockMvc
.perform(formLogin())
.andReturn();
// @formatter:on
Cookie cookie = result.getResponse().getCookie("SESSION");
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
T session = this.sessions.getSession(cookie.getValue());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo((int) TimeUnit.DAYS.toSeconds(30));
}
}
// end::class[]

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* @author rwinch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@SuppressWarnings("rawtypes")
public class RememberMeSecurityConfigurationXmlTests<T extends ExpiringSession> {
@Autowired
WebApplicationContext context;
@Autowired
SessionRepositoryFilter springSessionRepositoryFilter;
@Autowired
SessionRepository<T> sessions;
MockMvc mockMvc;
@Before
public void setup() {
// @formatter:off
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.context)
.addFilters(this.springSessionRepositoryFilter)
.apply(springSecurity())
.build();
// @formatter:on
}
@Test
public void authenticateWhenSpringSessionRememberMeEnabledThenCookieMaxAgeAndSessionExpirationSet()
throws Exception {
// @formatter:off
MvcResult result = this.mockMvc
.perform(formLogin())
.andReturn();
// @formatter:on
Cookie cookie = result.getResponse().getCookie("SESSION");
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
T session = this.sessions.getSession(cookie.getValue());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo((int) TimeUnit.DAYS.toSeconds(30));
}
}
// end::class[]

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2014-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package 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.FindByIndexNameSessionRepository;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
/**
* @author Joris Kuipers
*/
// tag::class[]
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private FindByIndexNameSessionRepository sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// other config goes here...
.sessionManagement()
.maximumSessions(2)
.sessionRegistry(sessionRegistry());
}
@Bean
@SuppressWarnings("unchecked")
public SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(this.sessionRepository);
}
}
// end::class[]

View File

@@ -1,28 +0,0 @@
<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- tag::config[] -->
<security:http>
<!-- ... -->
<security:form-login />
<security:remember-me services-ref="rememberMeServices"/>
</security:http>
<bean id="rememberMeServices"
class="org.springframework.session.security.web.authentication.SpringSessionRememberMeServices"
p:alwaysRemember="true"/>
<!-- end::config[] -->
<security:user-service>
<security:user name="user" password="password" authorities="ROLE_USER"/>
</security:user-service>
<bean class="org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration"/>
<bean id="springSessionRepository" class="org.springframework.session.MapSessionRepository"/>
</beans>

View File

@@ -1,22 +0,0 @@
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://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>

View File

@@ -59,7 +59,7 @@ cleanup_profile=_Spring Boot Cleanup Conventions
cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=Spring Session Java Conventions
formatter_profile=_Spring Boot Java Conventions
formatter_settings_version=12
org.eclipse.jdt.ui.exception.name=e
org.eclipse.jdt.ui.gettersetter.use.is=true

View File

@@ -1,31 +1,27 @@
bootstrapVersion=2.3.2
commonsPoolVersion=2.4.2
jacksonVersion=2.8.8
jacksonVersion=2.6.5
jspApiVersion=2.0
servletApiVersion=3.0.1
jstlelVersion=1.2.5
version=1.3.6.BUILD-SNAPSHOT
springDataRedisVersion=1.7.11.RELEASE
version=1.2.3.BUILD-SNAPSHOT
springDataRedisVersion=1.7.1.RELEASE
html5ShivVersion=3.7.3
commonsLoggingVersion=1.2
junitVersion=4.12
springDataRedisSpring3Version=1.7.1.RELEASE
lettuceVersion=3.5.0.Final
gebVersion=0.13.1
mockitoVersion=1.10.19
hazelcastVersion=3.6.8
hazelcastVersion=3.5.4
seleniumVersion=2.52.0
springDataGeodeVersion=1.0.0.INCUBATING-RELEASE
springSecurityVersion=4.2.11.RELEASE
springVersion=4.3.19.RELEASE
httpClientVersion=4.5.3
h2Version=1.4.195
jedisVersion=2.8.2
springDataMongoVersion=1.9.11.RELEASE
springSecurityVersion=4.0.3.RELEASE
springVersion=4.2.5.RELEASE
httpClientVersion=4.5.1
jedisVersion=2.8.1
h2Version=1.4.191
springDataMongoVersion=1.9.1.RELEASE
springShellVersion=1.1.0.RELEASE
springDataGemFireVersion=1.8.11.RELEASE
assertjVersion=2.5.0
springDataGemFireVersion=1.8.1.RELEASE
assertjVersion=2.3.0
spockVersion=1.0-groovy-2.4
webjarsTaglibVersion=0.3
jstlVersion=1.2.1
groovyVersion=2.4.11
groovyVersion=2.4.4

View File

@@ -1,3 +0,0 @@
sonarqube {
skipProject = true
}

View File

@@ -45,9 +45,6 @@ task cleanEclipseJdtUi(type: Delete) {
delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml")
}
task eclipseConfiguration(dependsOn: [eclipseCheckstyle, eclipseSettings, eclipseWstComponent]) {
}
tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare)
tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi)
tasks["eclipse"].dependsOn(eclipseConfiguration)
tasks["eclipse"].dependsOn(eclipseCheckstyle, eclipseSettings, eclipseWstComponent)

View File

@@ -14,7 +14,7 @@ group = 'org.springframework.session'
sourceCompatibility = 1.5
targetCompatibility = 1.5
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'Brussels-BUILD-SNAPSHOT'
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'latest.integration'
ext.spockDependencies = [
dependencies.create("org.spockframework:spock-core:$spockVersion") {
@@ -78,7 +78,7 @@ task integrationTest(type: Test, dependsOn: jar) {
check.dependsOn integrationTest
checkstyle {
configFile = rootProject.file('etc/checkstyle/checkstyle.xml')
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
configProperties.configDir = configFile.parentFile
toolVersion = '6.16.1'
}

View File

@@ -9,9 +9,6 @@ configurations.spring3TestRuntime {
&& details.requested.name != 'spring-messaging') {
details.useVersion '3.2.14.RELEASE'
}
if (details.requested.name == 'spring-data-redis') {
details.useVersion springDataRedisSpring3Version
}
}
}

View File

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

View File

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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Mon Jan 29 18:35:20 CET 2018
#Tue Nov 25 20:57:10 CST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip

74
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
#!/usr/bin/env bash
##############################################################################
##
@@ -6,30 +6,12 @@
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="-Xmx1024M -XX:MaxPermSize=512M"
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -48,7 +30,6 @@ die ( ) {
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -59,11 +40,31 @@ case "`uname`" in
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -89,7 +90,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -113,7 +114,6 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,19 +154,11 @@ if $cygwin ; then
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
APP_ARGS=$(save "$@")
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

14
gradlew.bat vendored
View File

@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=-Xmx1024M -XX:MaxPermSize=512M
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,9 +46,10 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -59,6 +60,11 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@@ -7,26 +7,24 @@ buildscript {
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
apply from: SAMPLE_GRADLE
group = 'samples'
ext['spring-security.version'] = springSecurityVersion
dependencies {
compile project(':spring-session'),
"org.springframework.boot:spring-boot-starter-data-redis",
"org.springframework.boot:spring-boot-starter-redis",
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"org.springframework.boot:spring-boot-starter-security",
"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"
testCompile "org.springframework.boot:spring-boot-starter-test"

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="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
@@ -65,11 +65,11 @@
margin-left: 1em;
}
</style>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>

View File

@@ -3,13 +3,9 @@ apply from: TOMCAT_7_GRADLE
apply from: SAMPLE_GRADLE
dependencies {
compile(project(':spring-session-data-redis')) {
exclude module: 'jedis'
}
compile "org.springframework:spring-web:$springVersion",
"biz.paluch.redis:lettuce:$lettuceVersion",
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"

View File

@@ -17,7 +17,7 @@
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@@ -26,8 +26,8 @@ import org.springframework.session.web.http.DefaultCookieSerializer;
public class Config {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
// tag::cookie-serializer[]

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -7,26 +7,24 @@ buildscript {
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
apply from: SAMPLE_GRADLE
group = 'samples'
ext['spring-security.version'] = springSecurityVersion
dependencies {
compile project(':spring-session'),
"org.springframework.boot:spring-boot-starter-data-redis",
"org.springframework.boot:spring-boot-starter-redis",
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-security",
"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",
"org.apache.httpcomponents:httpclient"

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="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
@@ -65,11 +65,11 @@
margin-left: 1em;
}
</style>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>

View File

@@ -1,70 +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"
}
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 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
grailsVersion = project.grailsVersion
gradleWrapperVersion = project.gradleWrapperVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencyManagement {
imports {
mavenBom "org.grails:grails-bom:$grailsVersion"
}
applyMavenExclusions false
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-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.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'
compile 'org.grails.plugins:spring-security-core:3.0.4'
}
task wrapper(type: Wrapper) {
gradleVersion = gradleWrapperVersion
}
assets {
minifyJs = true
minifyCss = true
minifyJs = true
minifyCss = true
}

View File

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

View File

@@ -8,7 +8,6 @@ dependencies {
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
"com.hazelcast:hazelcast-client:$hazelcastVersion",
jstlDependencies

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.config.SerializerConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.util.SocketUtils;
// tag::class[]
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 300)
@Configuration
public class Config {
@Bean(destroyMethod = "shutdown")
public HazelcastInstance hazelcastInstance() {
com.hazelcast.config.Config cfg = new com.hazelcast.config.Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
System.out.println("Hazelcast port #: " + netConfig.getPort());
cfg.setNetworkConfig(netConfig);
SerializerConfig serializer = new SerializerConfig().setTypeClass(Object.class)
.setImplementation(new ObjectStreamSerializer());
cfg.getSerializationConfig().addSerializerConfig(serializer);
return Hazelcast.newHazelcastInstance(cfg);
}
}
// end::class[]

View File

@@ -22,7 +22,7 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, SessionConfig.class);
super(SecurityConfig.class, Config.class);
}
}
// end::class[]

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapAttributeConfig;
import com.hazelcast.config.MapIndexConfig;
import com.hazelcast.config.SerializerConfig;
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 org.springframework.util.SocketUtils;
// tag::class[]
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 300)
@Configuration
public class SessionConfig {
@Bean(destroyMethod = "shutdown")
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
int port = SocketUtils.findAvailableTcpPort();
config.getNetworkConfig()
.setPort(port)
.getJoin().getMulticastConfig().setEnabled(false);
System.out.println("Hazelcast port #: " + port);
SerializerConfig serializer = new SerializerConfig()
.setImplementation(new ObjectStreamSerializer())
.setTypeClass(Object.class);
config.getSerializationConfig()
.addSerializerConfig(serializer);
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
config.getMapConfig("spring:session:sessions")
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config);
}
}
// end::class[]

View File

@@ -1,11 +1,9 @@
<%@ 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>Secured Content</title>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -5,7 +5,6 @@ apply from: SAMPLE_GRADLE
dependencies {
compile project(':spring-session'),
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
"com.hazelcast:hazelcast-client:$hazelcastVersion",
jstlDependencies

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 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.
@@ -52,7 +52,6 @@ public class Initializer implements ServletContextListener {
Config cfg = new Config();
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(getAvailablePort());
netConfig.getJoin().getMulticastConfig().setEnabled(false);
cfg.setNetworkConfig(netConfig);
SerializerConfig serializer = new SerializerConfig().setTypeClass(Object.class)
.setImplementation(new ObjectStreamSerializer());

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -9,26 +9,28 @@ buildscript {
apply from: JAVA_GRADLE
apply plugin: "application"
apply plugin: 'org.springframework.boot'
apply from: SAMPLE_GRADLE
apply plugin: 'spring-boot'
ext['spring-security.version'] = springSecurityVersion
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarqube {
skipProject = true
}
dependencies {
compile project(':spring-session-data-gemfire'),
"org.springframework.boot:spring-boot-starter-thymeleaf",
"org.springframework.boot:spring-boot-starter-web",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-locator"
"org.webjars:bootstrap:$bootstrapVersion"
runtime "org.springframework.shell:spring-shell:$springShellVersion"
runtime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
testCompile "org.springframework.boot:spring-boot-starter-test"
integrationTestCompile gebDependencies,
"org.spockframework:spock-spring:$spockVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:$springShellVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
}
run {
@@ -41,26 +43,24 @@ springBoot {
mainClass = 'sample.client.Application'
}
task runGemFireServer() {
doLast {
println 'STARTING GEMFIRE SERVER...'
task runGemFireServer() << {
println 'STARTING GEMFIRE SERVER...'
ext.port = reservePort()
ext.port = reservePort()
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String[] commandLine = ['java', '-server', '-ea', '-classpath', classpath,
"-Dgemfire.cache.server.port=$port",
"-Dgemfire.log-level=" + System.getProperty('gemfire.log.level', 'warning'),
'sample.server.GemFireServer']
String[] commandLine = ['java', '-server', '-ea', '-classpath', classpath,
"-Dgemfire.cache.server.port=$port",
"-Dgemfire.log-level=" + System.getProperty('gemfire.log.level', 'warning'),
'sample.server.GemFireServer']
//println commandLine
//println commandLine
ext.process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}
ext.process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}

View File

@@ -16,6 +16,8 @@
package sample.client;
import java.io.IOException;
import java.net.Socket;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -24,6 +26,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpSession;
@@ -31,6 +34,8 @@ import com.gemstone.gemfire.cache.client.Pool;
import com.gemstone.gemfire.management.membership.ClientMembership;
import com.gemstone.gemfire.management.membership.ClientMembershipEvent;
import com.gemstone.gemfire.management.membership.ClientMembershipListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
@@ -42,8 +47,8 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
@@ -54,14 +59,16 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* A Spring Boot, GemFire cache client, web application that reveals the current state of the HTTP Session.
* A Spring Boot-based GemFire cache client web application that reveals the current state
* of the HTTP Session.
*
* @author John Blum
* @see javax.servlet.http.HttpSession
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.context.annotation.Bean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* EnableGemFireHttpSession
* @see org.springframework.stereotype.Controller
* @see com.gemstone.gemfire.cache.client.ClientCache
* @since 1.2.1
@@ -72,42 +79,63 @@ import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class Application {
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(60);
static final int MAX_CONNECTIONS = 50;
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
static final long DEFAULT_WAIT_INTERVAL = 500L;
static final CountDownLatch latch = new CountDownLatch(1);
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
static final String INDEX_TEMPLATE_VIEW_NAME = "index";
static final String PING_RESPONSE = "PONG";
static final String REQUEST_COUNT_ATTRIBUTE_NAME = "requestCount";
static { // <6>
ClientMembership
.registerClientMembershipListener(new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
}
});
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
protected final Logger logger = LoggerFactory.getLogger(getClass());
@Bean
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
String applicationName() {
return "samples:httpsession-gemfire-boot-"
.concat(Application.class.getSimpleName());
}
String gemfireLogLevel() {
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
}
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
}
Properties gemfireProperties() { // <2>
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("log-level", gemfireLogLevel());
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-boot:"
.concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
}
@Bean
ClientCacheFactoryBean gemfireCache() { // <3>
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
@@ -125,23 +153,18 @@ public class Application {
PoolFactoryBean gemfirePool = new PoolFactoryBean();
gemfirePool.setKeepAlive(false);
gemfirePool.setPingInterval(TimeUnit.SECONDS.toMillis(5));
gemfirePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(15)).intValue());
gemfirePool.setMaxConnections(MAX_CONNECTIONS);
gemfirePool.setPingInterval(TimeUnit.SECONDS.toMillis(15));
gemfirePool.setRetryAttempts(1);
gemfirePool.setSubscriptionEnabled(true);
gemfirePool.setThreadLocalConnections(false);
gemfirePool.setServers(Collections.singleton(newConnectionEndpoint(host, port)));
gemfirePool.setServerEndpoints(
Collections.singleton(newConnectionEndpoint(host, port)));
return gemfirePool;
}
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
}
@Bean
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor(
BeanPostProcessor gemfireCacheServerAvailabilityBeanPostProcessor(
@Value("${gemfire.cache.server.host:localhost}") final String host,
@Value("${gemfire.cache.server.port:12480}") final int port) { // <5>
@@ -149,15 +172,12 @@ public class Application {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if ("gemfirePool".equals(beanName)) {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
latch.countDown();
}
});
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
if (!waitForCacheServerToStart(host, port)) {
Application.this.logger.warn(
"No GemFire Cache Server found on [host: {}, port: {}]",
host, port);
}
}
return bean;
@@ -165,12 +185,13 @@ public class Application {
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
try {
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
host, port));
Assert.state(
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
String.format(
"GemFire Cache Server failed to start on [host: %1$s, port: %2$d]",
host, port));
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -183,23 +204,23 @@ public class Application {
}
@RequestMapping("/")
public String index() { // <6>
public String index() { // <7>
return INDEX_TEMPLATE_VIEW_NAME;
}
@RequestMapping(method = RequestMethod.GET, path = "/ping")
@ResponseBody
public String ping() { // <7>
public String ping() { // <8>
return PING_RESPONSE;
}
@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) { // <8>
@RequestParam(name = "attributeValue", required = false) String value) { // <9>
modelMap.addAttribute("sessionAttributes",
attributes(setAttribute(updateRequestCount(session), name, value)));
attributes(setAttribute(updateRequestCount(session), name, value)));
return INDEX_TEMPLATE_VIEW_NAME;
}
@@ -209,24 +230,27 @@ public class Application {
@SuppressWarnings("all")
HttpSession updateRequestCount(HttpSession session) {
synchronized (session) {
Integer currentRequestCount = (Integer) session.getAttribute(REQUEST_COUNT_ATTRIBUTE_NAME);
session.setAttribute(REQUEST_COUNT_ATTRIBUTE_NAME, nullSafeIncrement(currentRequestCount));
Integer currentRequestCount = (Integer) session
.getAttribute(REQUEST_COUNT_ATTRIBUTE_NAME);
session.setAttribute(REQUEST_COUNT_ATTRIBUTE_NAME,
nullSafeIncrement(currentRequestCount));
return session;
}
}
/* (non-Javadoc) */
Integer nullSafeIncrement(Integer value) {
return (nullSafeIntValue(value) + 1);
return (nullSafeInt(value) + 1);
}
/* (non-Javadoc) */
int nullSafeIntValue(Number value) {
int nullSafeInt(Number value) {
return (value != null ? value.intValue() : 0);
}
/* (non-Javadoc) */
HttpSession setAttribute(HttpSession session, String attributeName, String attributeValue) {
HttpSession setAttribute(HttpSession session, String attributeName,
String attributeValue) {
if (isSet(attributeName, attributeValue)) {
session.setAttribute(attributeName, attributeValue);
}
@@ -245,25 +269,97 @@ public class Application {
return set;
}
/* (non-Javadoc) */
Map<String, String> attributes(HttpSession session) {
Map<String, String> sessionAttributes = new HashMap<String, String>();
for (String attributeName : toIterable(session.getAttributeNames())) {
sessionAttributes.put(attributeName,
String.valueOf(session.getAttribute(attributeName)));
String.valueOf(session.getAttribute(attributeName)));
}
return sessionAttributes;
}
/* (non-Javadoc) */
<T> Iterable<T> toIterable(final Enumeration<T> enumeration) {
return new Iterable<T>() {
public Iterator<T> iterator() {
return (enumeration == null ? Collections.<T>emptyIterator()
: CollectionUtils.toIterator(enumeration));
: new Iterator<T>() {
public boolean hasNext() {
return enumeration.hasMoreElements();
}
public T next() {
return enumeration.nextElement();
}
public void remove() {
throw new UnsupportedOperationException(
"Auto-generated method stub");
}
});
}
};
}
/* (non-Javadoc) */
boolean waitForCacheServerToStart(String host, int port) {
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
boolean waitForCacheServerToStart(final String host, final int port, long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
public boolean evaluate() {
Socket socket = null;
try {
// NOTE: this code is not intended to be an atomic, compound action (a
// possible race condition);
// opening another connection (at the expense of using system
// resources) after connectivity
// has already been established is not detrimental in this use case
if (!this.connected.get()) {
socket = new Socket(host, port);
this.connected.set(true);
}
}
catch (IOException ignore) {
}
finally {
GemFireUtils.close(socket);
}
return this.connected.get();
}
}, duration);
}
boolean waitOnCondition(Condition condition) {
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
}
@SuppressWarnings("all")
boolean waitOnCondition(Condition condition, long duration) {
final long timeout = (System.currentTimeMillis() + duration);
try {
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
synchronized (condition) {
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
}
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return condition.evaluate();
}
interface Condition {
boolean evaluate();
}
}

View File

@@ -47,7 +47,7 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 20) // <1>
public class GemFireServer {
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(GemFireServer.class);
@@ -60,26 +60,25 @@ public class GemFireServer {
return new PropertySourcesPlaceholderConfigurer();
}
String applicationName() {
return "samples:httpsession-gemfire-boot-"
.concat(GemFireServer.class.getSimpleName());
}
String gemfireLogLevel() {
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
}
Properties gemfireProperties() { // <2>
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", logLevel());
//gemfireProperties.setProperty("jmx-manager", "true");
//gemfireProperties.setProperty("jmx-manager-start", "true");
gemfireProperties.setProperty("log-level", gemfireLogLevel());
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-boot:".concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
}
@Bean
CacheFactoryBean gemfireCache() { // <3>
CacheFactoryBean gemfireCache = new CacheFactoryBean();
@@ -99,11 +98,12 @@ public class GemFireServer {
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
gemfireCacheServer.setAutoStartup(true);
gemfireCacheServer.setBindAddress(bindAddress);
gemfireCacheServer.setCache(gemfireCache);
gemfireCacheServer.setBindAddress(bindAddress);
gemfireCacheServer.setHostNameForClients(hostnameForClients);
gemfireCacheServer.setMaxTimeBetweenPings(
Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
Long.valueOf(TimeUnit.MINUTES.toMillis(1)).intValue());
gemfireCacheServer.setNotifyBySubscription(true);
gemfireCacheServer.setPort(port);
return gemfireCacheServer;

View File

@@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Session Attributes</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/bootstrap/2.2.2/css/bootstrap.min.css"/>
<style type="text/css">
body {
padding: 1em;

View File

@@ -1,24 +1,26 @@
apply from: JAVA_GRADLE
apply from: TOMCAT_7_GRADLE
apply plugin: "application"
apply from: SAMPLE_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarqube {
skipProject = true
}
dependencies {
compile project(':spring-session-data-gemfire'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
runtime "org.springframework.shell:spring-shell:$springShellVersion"
testCompile "junit:junit:$junitVersion"
integrationTestCompile gebDependencies
integrationTestRuntime "org.springframework.shell:spring-shell:$springShellVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
}
mainClassName = 'sample.Application'
@@ -26,33 +28,29 @@ mainClassName = 'sample.Application'
def port
def process
task availablePort() {
doLast {
def serverSocket = new ServerSocket(0)
port = serverSocket.localPort
serverSocket.close()
}
task availablePort() << {
def serverSocket = new ServerSocket(0)
port = serverSocket.localPort
serverSocket.close()
}
task runGemFireServer(dependsOn: availablePort) {
doLast {
println 'STARTING GEMFIRE SERVER...'
task runGemFireServer(dependsOn: availablePort) << {
println 'STARTING GEMFIRE SERVER...'
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String[] commandLine = ['java', '-server', '-ea',
"-Dspring.session.data.gemfire.port=$port",
"-Dsample.httpsession.gemfire.log-level="
+ System.getProperty('sample.httpsession.gemfire.log-level', 'warning'),
'-classpath', classpath, 'sample.Application']
String[] commandLine = ['java', '-server', '-ea',
"-Dspring.session.data.gemfire.port=$port",
"-Dsample.httpsession.gemfire.log-level="
+ System.getProperty('sample.httpsession.gemfire.log-level', 'warning'),
'-classpath', classpath, 'sample.Application' ]
//println commandLine
//println commandLine
process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}
process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}
integrationTest.doLast {

View File

@@ -27,8 +27,9 @@ import org.springframework.context.annotation.ImportResource;
public class Application {
public static void main(final String[] args) {
new AnnotationConfigApplicationContext(Application.class)
.registerShutdownHook();
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Application.class);
context.registerShutdownHook();
}
}
// tag::end[]

View File

@@ -16,8 +16,14 @@
package sample;
import java.io.IOException;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Resource;
import com.gemstone.gemfire.cache.client.Pool;
import com.gemstone.gemfire.management.membership.ClientMembership;
@@ -27,42 +33,53 @@ import com.gemstone.gemfire.management.membership.ClientMembershipListenerAdapte
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.util.Assert;
public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcessor {
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
static final long DEFAULT_WAIT_INTERVAL = 500L;
static final CountDownLatch latch = new CountDownLatch(1);
static final String DEFAULT_SERVER_HOST = "localhost";
@Value("${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}")
int port;
@Value("${application.gemfire.client-server.host:localhost}")
String host;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("gemfirePool".equals(beanName)) {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
// tag::class[]
static {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
public void memberJoined(final ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
});
}
});
}
@SuppressWarnings("all")
@Resource(name = "applicationProperties")
private Properties applicationProperties;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
String host = getServerHost(DEFAULT_SERVER_HOST);
Assert.isTrue(waitForCacheServerToStart(host, this.port),
String.format("GemFire Server failed to start [host: '%1$s', port: %2$d]%n",
host, this.port));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
try {
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
this.host, this.port));
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -72,4 +89,70 @@ public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcess
return bean;
}
// tag::end[]
interface Condition {
boolean evaluate();
}
String getServerHost(String defaultServerHost) {
return this.applicationProperties
.getProperty("application.gemfire.client-server.host", defaultServerHost);
}
boolean waitForCacheServerToStart(String host, int port) {
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
boolean waitForCacheServerToStart(final String host, final int port, long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
public boolean evaluate() {
Socket socket = null;
try {
// NOTE: this code is not intended to be an atomic, compound action (a
// possible race condition);
// opening another connection (at the expense of using system
// resources) after connectivity
// has already been established is not detrimental in this use case
if (!connected.get()) {
socket = new Socket(host, port);
connected.set(true);
}
}
catch (IOException ignore) {
}
finally {
GemFireUtils.close(socket);
}
return connected.get();
}
}, duration);
}
boolean waitOnCondition(Condition condition) {
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
}
@SuppressWarnings("all")
boolean waitOnCondition(Condition condition, long duration) {
final long timeout = (System.currentTimeMillis() + duration);
try {
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
synchronized (condition) {
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
}
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return condition.evaluate();
}
}

View File

@@ -1,2 +1,3 @@
application.gemfire.client-server.host=localhost
application.gemfire.client-server.port=12480
application.gemfire.client-server.port=11235
application.gemfire.client-server.max-connections=50

View File

@@ -20,6 +20,10 @@
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
<!--3-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
<!--4-->
<util:properties id="gemfireProperties">
<prop key="name">GemFireClientServerHttpSessionXmlSample</prop>
<prop key="mcast-port">0</prop>
@@ -28,18 +32,15 @@
<prop key="jmx-manager-start">true</prop>
</util:properties>
<!--4-->
<gfe:cache properties-ref="gemfireProperties"/>
<!--5-->
<gfe:cache-server auto-startup="true"
bind-address="${application.gemfire.client-server.host}"
host-name-for-clients="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
<gfe:cache properties-ref="gemfireProperties"
use-bean-factory-locator="false"/>
<!--6-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
<gfe:cache-server auto-startup="true"
bind-address="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"
max-connections="${application.gemfire.client-server.max-connections}"/>
<!-- end::beans[] -->
</beans>

View File

@@ -14,36 +14,41 @@
<!-- tag::beans[] -->
<!--1-->
<context:annotation-config/>
<util:properties id="applicationProperties"
location="classpath:META-INF/spring/application.properties"/>
<!--2-->
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
<context:property-placeholder properties-ref="applicationProperties"/>
<!--3-->
<bean class="sample.GemFireCacheServerReadyBeanPostProcessor"/>
<context:annotation-config/>
<!--4-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
<!--5-->
<bean class="sample.GemFireCacheServerReadyBeanPostProcessor"/>
<!--6-->
<util:properties id="gemfireProperties">
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
</util:properties>
<!--5-->
<gfe:client-cache properties-ref="gemfireProperties" pool-name="gemfirePool"/>
<gfe:client-cache properties-ref="gemfireProperties"/>
<!--6-->
<gfe:pool keep-alive="false"
<!--7-->
<gfe:pool free-connection-timeout="5000"
keep-alive="false"
ping-interval="5000"
read-timeout="5000"
retry-attempts="1"
retry-attempts="2"
subscription-enabled="true"
thread-local-connections="false">
thread-local-connections="false"
max-connections="${application.gemfire.client-server.max-connections}">
<gfe:server host="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
</gfe:pool>
<!--7-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30" p:poolName="DEFAULT"/>
<!-- end::beans[] -->
</beans>

View File

@@ -23,6 +23,7 @@
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- end::springSessionRepositoryFilter[] -->

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -7,18 +7,15 @@ dependencies {
compile project(':spring-session-data-gemfire'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
runtime "org.springframework.shell:spring-shell:$springShellVersion"
testCompile "junit:junit:$junitVersion"
integrationTestCompile gebDependencies
integrationTestRuntime "org.springframework.shell:spring-shell:$springShellVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
}
@@ -27,33 +24,29 @@ def process
mainClassName = "sample.ServerConfig"
task availablePort() {
doLast {
def serverSocket = new ServerSocket(0)
port = serverSocket.localPort
serverSocket.close()
}
task availablePort() << {
def serverSocket = new ServerSocket(0)
port = serverSocket.localPort
serverSocket.close()
}
task runGemFireServer(dependsOn: availablePort) {
doLast {
println 'STARTING GEMFIRE SERVER...'
task runGemFireServer(dependsOn: availablePort) << {
println 'STARTING GEMFIRE SERVER...'
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String[] commandLine = ['java', '-server', '-ea',
"-Dspring.session.data.gemfire.port=$port",
"-Dsample.httpsession.gemfire.log-level="
+ System.getProperty('sample.httpsession.gemfire.log-level', 'warning'),
'-classpath', classpath, 'sample.ServerConfig']
String[] commandLine = ['java', '-server', '-ea',
"-Dspring.session.data.gemfire.port=$port",
"-Dsample.httpsession.gemfire.log-level="
+ System.getProperty('sample.httpsession.gemfire.log-level', 'warning'),
'-classpath', classpath, 'sample.ServerConfig']
//println commandLine
//println commandLine
process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}
process = commandLine.execute()
process.in.close()
process.out.close()
process.err.close()
}
integrationTest.doLast {

View File

@@ -16,10 +16,13 @@
package sample;
import java.io.IOException;
import java.net.Socket;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import com.gemstone.gemfire.cache.client.Pool;
import com.gemstone.gemfire.management.membership.ClientMembership;
@@ -32,97 +35,99 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.util.Assert;
// tag::class[]
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30, poolName = "DEFAULT") // <1>
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <1>
public class ClientConfig {
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
static final long DEFAULT_WAIT_INTERVAL = 500L;
static final CountDownLatch latch = new CountDownLatch(1);
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
static {
System.setProperty("gemfire.log-level", logLevel());
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
public void memberJoined(ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
}
});
}
private static String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
}
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() { // <2>
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("log-level", logLevel());
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-clientserver:"
.concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level",
DEFAULT_GEMFIRE_LOG_LEVEL);
return new Properties();
}
@Bean
ClientCacheFactoryBean gemfireCache(
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) { // <3>
ClientCacheFactoryBean gemfireCache() { // <4>
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
// GemFire Pool settings <4>
clientCacheFactory.setKeepAlive(false);
clientCacheFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
clientCacheFactory.setReadTimeout(2000); // 2 seconds
clientCacheFactory.setRetryAttempts(1);
clientCacheFactory.setSubscriptionEnabled(true);
clientCacheFactory.setThreadLocalConnections(false);
clientCacheFactory.setServers(Collections.singletonList(
newConnectionEndpoint(ServerConfig.SERVER_HOST, port)));
return clientCacheFactory;
}
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
@Bean
PoolFactoryBean gemfirePool(// <3>
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMaxConnections(ServerConfig.MAX_CONNECTIONS);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(2);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setThreadLocalConnections(false);
poolFactory.setServers(Collections.singletonList(
new ConnectionEndpoint(ServerConfig.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor() { // <5>
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor(// <5>
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") final int port) {
return new BeanPostProcessor() {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if ("gemfirePool".equals(beanName)) {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
latch.countDown();
}
});
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
Assert.isTrue(waitForCacheServerToStart(ServerConfig.SERVER_HOSTNAME, port),
String.format("GemFire Server failed to start [hostname: %1$s, port: %2$d]",
ServerConfig.SERVER_HOSTNAME, port));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
try {
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
ServerConfig.SERVER_HOST, ServerConfig.SERVER_PORT));
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -134,4 +139,65 @@ public class ClientConfig {
};
}
// end::class[]
interface Condition {
boolean evaluate();
}
boolean waitForCacheServerToStart(String host, int port) {
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
boolean waitForCacheServerToStart(final String host, final int port, long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
public boolean evaluate() {
Socket socket = null;
try {
// NOTE: this code is not intended to be an atomic, compound action (a
// possible race condition);
// opening another connection (at the expense of using system
// resources) after connectivity
// has already been established is not detrimental in this use case
if (!connected.get()) {
socket = new Socket(host, port);
connected.set(true);
}
}
catch (IOException ignore) {
}
finally {
GemFireUtils.close(socket);
}
return connected.get();
}
}, duration);
}
boolean waitOnCondition(Condition condition) {
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
}
@SuppressWarnings("all")
boolean waitOnCondition(Condition condition, long duration) {
final long timeout = (System.currentTimeMillis() + duration);
try {
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
synchronized (condition) {
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
}
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return condition.evaluate();
}
}

View File

@@ -18,7 +18,6 @@ package sample;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import com.gemstone.gemfire.cache.Cache;
@@ -34,26 +33,21 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <1>
public class ServerConfig {
static final int MAX_CONNECTIONS = 50;
static final int SERVER_PORT = 12480;
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
static final String SERVER_HOST = "localhost";
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException { // <5>
new AnnotationConfigApplicationContext(ServerConfig.class)
.registerShutdownHook();
}
static final String SERVER_HOSTNAME = "localhost";
@Bean
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() { // <2>
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("name", "GemFireClientServerHttpSessionSample");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("jmx-manager", "true");
@@ -62,14 +56,8 @@ public class ServerConfig {
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-clientserver:"
.concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level",
DEFAULT_GEMFIRE_LOG_LEVEL);
private String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
}
@Bean
@@ -83,19 +71,25 @@ public class ServerConfig {
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) { // <4>
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache, // <4>
@Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) {
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
gemfireCacheServer.setAutoStartup(true);
gemfireCacheServer.setBindAddress(SERVER_HOST);
gemfireCacheServer.setCache(gemfireCache);
gemfireCacheServer.setHostNameForClients(SERVER_HOST);
gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
gemfireCacheServer.setPort(port);
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setHostNameForClients(SERVER_HOSTNAME);
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
cacheServerFactory.setPort(port);
return gemfireCacheServer;
return cacheServerFactory;
}
@SuppressWarnings("resource")
public static void main(final String[] args) throws IOException { // <5>
new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook();
}
}
// end::class[]

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -6,7 +6,6 @@ dependencies {
compile project(':spring-session-data-gemfire'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
@@ -15,5 +14,5 @@ dependencies {
integrationTestCompile gebDependencies
integrationTestRuntime "org.springframework.shell:spring-shell:$springShellVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
}

View File

@@ -23,8 +23,9 @@
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- end::springSessionRepositoryFilter[] -->
@@ -54,4 +55,4 @@
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
</web-app>

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -6,7 +6,6 @@ dependencies {
compile project(':spring-session-data-gemfire'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
@@ -15,5 +14,5 @@ dependencies {
integrationTestCompile gebDependencies
integrationTestRuntime "org.springframework.shell:spring-shell:$springShellVersion"
integrationTestRuntime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
}

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -7,27 +7,25 @@ buildscript {
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
apply from: SAMPLE_GRADLE
group = 'samples'
ext['spring-security.version'] = springSecurityVersion
dependencies {
compile project(':spring-session-jdbc'),
"org.springframework.boot:spring-boot-starter-jdbc",
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-security",
"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",
"com.h2database:h2"
"com.h2database:h2",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion"
testCompile "org.springframework.boot:spring-boot-starter-test"

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="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
/* Sticky footer styles
-------------------------------------------------- */
@@ -65,11 +65,11 @@
margin-left: 1em;
}
</style>
<link th:href="@{/webjars/bootstrap/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<link th:href="@{/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css}" href="/webjars/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script th:src="@{/webjars/html5shiv/html5shiv.min.js}" src="/webjars/html5shiv/html5shiv.min.js"></script>
<script th:src="@{/webjars/html5shiv/3.7.3/html5shiv.min.js}" src="/webjars/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>

View File

@@ -1,12 +1,11 @@
apply from: JAVA_GRADLE
apply from: TOMCAT_7_GRADLE
apply from: TOMCAT_6_GRADLE
apply from: SAMPLE_GRADLE
dependencies {
compile project(':spring-session-jdbc'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
"com.h2database:h2:$h2Version",
jstlDependencies

View File

@@ -23,8 +23,9 @@
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- end::springSessionRepositoryFilter[] -->

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -6,7 +6,6 @@ dependencies {
compile project(':spring-session-jdbc'),
"org.springframework:spring-web:$springVersion",
"org.webjars:bootstrap:$bootstrapVersion",
"org.webjars:webjars-taglib:$webjarsTaglibVersion",
"com.h2database:h2:$h2Version",
jstlDependencies

View File

@@ -1,11 +1,9 @@
<%@ 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>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<link rel="stylesheet" href="<c:url value="/webjars/bootstrap/2.3.2/css/bootstrap.min.css"/>">
<style type="text/css">
body {
padding: 1em;

View File

@@ -1,67 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
}
apply plugin: 'org.springframework.boot'
apply from: JAVA_GRADLE
apply from: SAMPLE_GRADLE
group = 'samples'
ext {
jsonassertVersion="1.3.0"
}
ext['spring-security.version'] = springSecurityVersion
dependencies {
compile project(':spring-session'),
"org.springframework.boot:spring-boot-starter-data-redis",
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"org.springframework.boot:spring-boot-starter-security",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"biz.paluch.redis:lettuce:$lettuceVersion",
"org.apache.httpcomponents:httpclient"
testCompile "org.springframework.boot:spring-boot-starter-test",
"org.assertj:assertj-core:$assertjVersion"
testCompile "org.skyscreamer:jsonassert:$jsonassertVersion"
testCompile "org.assertj:assertj-core:$assertjVersion"
integrationTestCompile gebDependencies,
"org.spockframework:spock-spring:$spockVersion"
}
//
integrationTest {
doFirst {
def port = reservePort()
def host = 'localhost:' + port
systemProperties['geb.build.baseUrl'] = 'http://'+host+'/'
systemProperties['geb.build.reportsDir'] = 'build/geb-reports'
systemProperties['server.port'] = port
systemProperties['management.port'] = 0
systemProperties['spring.session.redis.namespace'] = project.name
}
jvmArgs "-XX:-UseSplitVerifier"
}
integrationTest {
testLogging {
events "passed", "skipped", "failed"
}
}
def reservePort() {
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
}

View File

@@ -1,54 +0,0 @@
package samples
import geb.spock.*
import sample.Application
import samples.pages.*
import spock.lang.Stepwise
import org.springframework.boot.test.IntegrationTest
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.web.WebAppConfiguration
/**
* @author jitendra on 15/3/16.
*/
@Stepwise
@ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader)
@WebAppConfiguration
@IntegrationTest
class HttpRedisJsonTest extends GebSpec {
def'login page test'() {
when:
to LoginPage
then:
at LoginPage
}
def"Unauthenticated user sent to login page"() {
when:
via HomePage
then:
at LoginPage
}
def"Successful Login test"() {
when:
login()
then:
at HomePage
driver.manage().cookies.find {it.name == "SESSION"}
!driver.manage().cookies.find {it.name == "JSESSIONID"}
}
def"Set and get attributes in session"() {
when:
setAttribute("Demo Key", "Demo Value")
then:
at SetAttributePage
tdKey()*.text().contains("Demo Key")
tdKey()*.text().contains("Demo Value")
}
}

View File

@@ -1,26 +0,0 @@
package samples.pages
import geb.Page
/**
* @author jitendra on 15/3/16.
*/
class HomePage extends Page {
static url="/"
static at=
{
driver.title == "Spring Session Sample - Home"
}
static content=
{
form { $('form') }
submit { $('button[type=submit]') }
setAttribute(required: false) { key = 'project', value = 'SessionRedisJson' ->
form.key = key
form.value = value
submit.click(SetAttributePage)
}
}
}

View File

@@ -1,27 +0,0 @@
package samples.pages
import geb.Page
/**
* @author jitendra on 15/3/16.
*/
class LoginPage extends Page {
static url="/login"
static at=
{
assert title == "Spring Session Sample - Login"
return true
}
static content=
{
form { $('form') }
submit { $('button[type=submit]') }
login(required: false) { user = 'user', pass = 'password' ->
form.username = user
form.password = pass
submit.click(HomePage)
}
}
}

View File

@@ -1,20 +0,0 @@
package samples.pages
import geb.Page
/**
* @author jitendra on 15/3/16.
*/
class SetAttributePage extends Page {
static url="/setValue"
static at=
{
title == "Spring Session Sample - Home"
}
static content=
{
tdKey { $('td') }
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package samples;
import org.junit.Test;
import org.junit.runner.RunWith;
import sample.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author jitendra on 8/3/16.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class RedisSerializerTest {
@Autowired
RedisTemplate<Object, Object> sessionRedisTemplate;
@Test
public void testRedisTemplate() {
assertThat(this.sessionRedisTemplate).isNotNull();
assertThat(this.sessionRedisTemplate.getDefaultSerializer()).isNotNull();
assertThat(this.sessionRedisTemplate.getDefaultSerializer())
.isInstanceOf(GenericJackson2JsonRedisSerializer.class);
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author jitendra on 3/3/16.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author jitendra on 3/3/16.
*/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout().permitAll();
}
// @formatter:on
// @formatter:off
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
// @formatter:on
}

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