diff --git a/buildSrc/src/main/groovy/build/GemFireServerPlugin.groovy b/buildSrc/src/main/groovy/build/GemFireServerPlugin.groovy index 410a850..655552c 100644 --- a/buildSrc/src/main/groovy/build/GemFireServerPlugin.groovy +++ b/buildSrc/src/main/groovy/build/GemFireServerPlugin.groovy @@ -13,7 +13,7 @@ class GemFireServerPlugin implements Plugin { project.tasks.create('gemfireServer', GemFireServerTask) project.tasks.integrationTest.doLast { - println 'Stopping GemFire Server...' + println 'Stopping Apache Geode Server...' project.tasks.gemfireServer.process?.destroy() // project.tasks.gemfireServer.process?.destroyForcibly() } @@ -22,7 +22,7 @@ class GemFireServerPlugin implements Plugin { dependsOn project.tasks.gemfireServer doFirst { project.gretty { - jvmArgs = ["-Dspring.session.data.gemfire.port=${project.tasks.gemfireServer.port}"] + jvmArgs = [ "-Dspring.session.data.geode.cache.server.port=${project.tasks.gemfireServer.port}" ] } } } @@ -46,20 +46,19 @@ class GemFireServerPlugin implements Plugin { def greet() { port = availablePort() - println "Starting GemFire Server on port [$port]..." + println "Starting Apache Geode Server on port [$port]..." def out = debug ? System.err : new StringBuilder() def err = debug ? System.err : new StringBuilder() String classpath = project.sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator) - String gemfireLogLevel = System.getProperty('spring.session.data.gemfire.log-level', 'warning') + String gemfireLogLevel = System.getProperty('spring.session.data.geode.log-level', 'warning') String[] commandLine = [ 'java', '-server', '-ea', '-classpath', classpath, //"-Dgemfire.log-file=gemfire-server.log", - //"-Dgemfire.log-level=config", - "-Dspring.session.data.gemfire.log-level=" + gemfireLogLevel, - "-Dspring.session.data.gemfire.port=${port}", + "-Dgemfire.log-level=" + gemfireLogLevel, + "-Dspring.session.data.geode.cache.server.port=${port}", mainClassName ] diff --git a/docs/spring-session-docs.gradle b/docs/spring-session-docs.gradle index eb4edb8..f23a1a7 100644 --- a/docs/spring-session-docs.gradle +++ b/docs/spring-session-docs.gradle @@ -23,14 +23,14 @@ def versions = dependencyManagement.managedVersions asciidoctor { def ghTag = snapshotBuild ? 'master' : project.version - def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag" + def ghUrl = "https://github.com/spring-projects/spring-session-data-geode/tree/$ghTag" attributes 'version-snapshot': snapshotBuild, 'version-milestone': milestoneBuild, 'version-release': releaseBuild, 'gh-url': ghUrl, 'gh-samples-url': "$ghUrl/samples/", - 'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip", + 'download-url' : "https://github.com/spring-projects/spring-session-data-geode/archive/${ghTag}.zip", 'spring-version' : versions['org.springframework:spring-core'], 'spring-session-version' : version, 'docs-itest-dir' : rootProject.projectDir.path + '/docs/src/integration-test/java/', diff --git a/docs/src/docs/asciidoc/guides/boot-gemfire.adoc b/docs/src/docs/asciidoc/guides/boot-gemfire.adoc index d07442e..4ee0724 100644 --- a/docs/src/docs/asciidoc/guides/boot-gemfire.adoc +++ b/docs/src/docs/asciidoc/guides/boot-gemfire.adoc @@ -1,19 +1,21 @@ -= Spring Session - HttpSession with GemFire Client/Server using Spring Boot += Spring Session - HttpSession with Apache Geode Client/Server using Spring Boot John Blum :toc: This guide describes how to build a _Spring Boot_ application configured with _Spring Session_ to transparently leverage -Pivotal GemFire to back a web application's `HttpSession`. +Apache Geode to manage a web application's `javax.servlet.http.HttpSession`. -In this sample, GemFire's client/server topology is employed using a pair of _Spring Boot_ applications, one to -configure and run a GemFire Server and another to configure and run the client, Spring MVC-based web application +In this sample, Apache Geode's client/server topology is employed using a pair of _Spring Boot_ applications, one to +configure and run a Geode Server and another to configure and run the cache client, Spring MVC-based web application making use of the `HttpSession`. -NOTE: The completed guide can be found in the <>. +NOTE: The completed guide can be found in the +<>. == Updating Dependencies + Before using _Spring Session_, you must ensure that the required dependencies are included. -If you are using Maven, include the following `dependencies` in your _pom.xml_: +If you are using _Maven_, include the following `dependencies` in your `pom.xml`: .pom.xml [source,xml] @@ -24,8 +26,8 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: org.springframework.session - spring-session-data-gemfire - {spring-session-version} + spring-session-data-geode + ${spring-session-data-geode-version} pom @@ -36,8 +38,8 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: ---- ifeval::["{version-snapshot}" == "true"] -Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a SNAPSHOT version, we need to add the _Spring_ Snapshot Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -54,8 +56,8 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] ifeval::["{version-milestone}" == "true"] -Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a Milestone version, we need to add the _Spring_ Milestone Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -75,127 +77,124 @@ endif::[] [[httpsession-spring-java-configuration-gemfire-boot]] == Spring Boot Configuration -After adding the required dependencies and repository declarations, we can create our Spring configuration -for both the GemFire client and server using _Spring Boot_. The Spring configuration is responsible for -creating a Servlet Filter that replaces the `HttpSession` with an implementation backed by _Spring Session_ -and GemFire. +After adding the required dependencies and repository declarations, we can create the _Spring_ configuration +for both our Apache Geode client and server using _Spring Boot_. The _Spring_ configuration is responsible for +creating a `Servlet Filter` that replaces the `HttpSession` with an implementation backed by _Spring Session_ +and Apace Geode. -=== Spring Boot-based GemFire Server +=== Spring Boot, Apache Geode Cache Server -We start with the _Spring Boot_ application for configuring and bootstrapping a GemFire Server process... +We start with a _Spring Boot_ application to configure and bootstrap the Apache Geode Server process... [source,java] ---- include::{samples-dir}boot/gemfire/src/main/java/sample/server/GemFireServer.java[tags=class] ---- -<1> The `@EnableGemFireHttpSession` annotation is used on the GemFire Server to mainly 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 -http://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html[GemFire's System properties]. -<3> Then, we create an instance of the GemFire `Cache` using our defined `Properties`. -<4> Finally, we 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. +<1> First, we annotate the Apache Geode Server configuration class with `@SpringBootApplication` to indicate that +this will be a _Spring Boot_ application in order to leverage all of _Spring Boot's_ features (e.g. _auto-configuration_). +<2> Next, we use the **new** _Spring Data Geode_ configuration annotation `@CacheServerApplication` to simplify +the creation of a peer cache instance along with a `CacheServer` for cache clients to connect. +<3> (Optional) Then, the `@EnableGemFireHttpSession` annotation is declared to create the necessary server-side `Region` +(by default, "_ClusteredSpringSessions_") used to store the `HttpSessions` state. This step is optional since the +Session `Region` could be created manually, perhaps using external means. Using `@EnableGemFireHttpSession` is convenient. +<4> Additionally, we enable the Apache Geode Manager embedded service, which allows JMX clients (e.g. Apache Geode's +_Gfsh_ shell tool) to connect to the server and inspect the configuration. +<5> Finally, we adjust the port that the `CacheServer` will use to listen for cache clients by declaring +a `CacheServerConfigurer` bean to modify the SDG `CacheServerFactoryBean` using property placeholders. -The sample also makes use of a `PropertySourcesPlaceholderConfigurer` bean in order to externalize the sample application -configuration to affect GemFire and application configuration/behavior from the command-line (e.g. such as GemFire's -`log-level` using the `gemfire.log.level` System property; more details below). +The sample also makes use of _Spring's_ `PropertySourcesPlaceholderConfigurer` in order to externalize +the sample application's configuration using a properties file or with JVM System properties, which ever. -=== Spring Boot-based GemFire cache client Web application +=== Spring Boot, Apache Geode 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 a _Spring Boot_ Web application to expose our Web service with _Spring_ MVC, running as an Apache Geode +cache client connected to our _Spring Boot_, Apache Geode Server. The Web application will use _Spring Session_ +backed by Apache Geode to manage Session state in a clustered (distributed), replicated fashion. [source,java] ---- include::{samples-dir}boot/gemfire/src/main/java/sample/client/Application.java[tags=class] ---- -<1> Here, again, we use the `@EnableGemFireHttpSession` annotation to not only configure the GemFire cache client, -but 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, -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* -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 -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, -the webapp stores an additional Session attribute (`requestCount`) to keep track of how many HTTP requests the user -has sent during the current "session". +<1> Again, we declare our Web application to be a _Spring Boot_ application by annotating our application class +with the `@SpringBootApplication` annotation. +<2> `@Controller` is a _Spring_ Web MVC annotation enabling our MVC handler mapping methods (i.e. methods annotated +with `@RequestMapping`) to process HTTP requests (e.g. <6>) +<3> We also declare our Web application to be a Geode cache client by annotating our application class with +`@ClientCacheApplication`. Additionally, we adjust a few basic, "DEFAULT" `Pool` settings. +<4> Next, we declare that the Web application will use _Spring Session_ backed by Apache Geode by annotating the +`ClientCacheConfiguration` class with `@EnableGemFireHttpSession`. This will create the necessary client-side `Region` +(by default, "ClusteredSpringSessions`, which is a `PROXY` `Region`) corresponding to the same server-side `Region` +by name. All Session state will be sent from the cache client Web application to the server through `Region` +data access operations. The client-side `Region` will use the "DEFAULT" `Pool`. +<5> Then, we adjust the port used by the cache client `Pool` to connect to the `CacheServer` +using a SDG `ClientCacheConfigurer`. +<6> We adjust the _Spring_ Web MVC configuration to set the home page, and... +<7> Finally, we declare the `/sessions` HTTP request handler method to set a HTTP Session attribute and increment +a count for the number of HTTP requests. -There are many other utility methods, so please refer to the actual source code for full details. +There are many other useful utility methods, so please refer to the actual source code for full details. -TIP: In typical GemFire deployments, where the cluster includes potentially hundreds or thousands of GemFire data nodes -(servers), it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator -passes meta-data to clients about the servers available, their load and which servers have the client's data of interest, -which is particularly important in direct, single-hop data access and latency-sensitive operations. See more details -about the http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide]. +TIP: In typical Geode production deployments, where the cluster includes potentially hundreds or thousands +of Geode servers (data nodes), it is more common for clients to connect to 1 or more Geode Locators running +in the cluster. A Locator passes meta-data to clients about the servers available in the cluster, the server load +and which servers have the client's data of interest, which is particularly important for direct, single-hop data access +and latency-sensitive operations. See more details about the +http://geode.apache.org/docs/guide/12/topologies_and_comm/cs_configuration/standard_client_server_deployment.html[Client/Server Deployment] +in the Apache Geode User Guide. -NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide]. +NOTE: For more information on configuring _Spring Data Geode, refer to the +http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[Reference Guide]. -The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both _Spring Session_ -and GemFire out-of-the-box using the following attributes: +`@EnableGemFireHttpSession` enables a developer to configure certain aspects of both _Spring Session_ and Apache Geode +out-of-the-box using the following attributes: +* `clientRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the client with a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/client/ClientRegionShortcut.html[ClientRegionShortcut] +(default is `PROXY`). This attribute is only used when configuring client `Region`. +* `indexableSessionAttributes` - Identifies the Session attributes by name that should be indexed for querying operations. +Only Session attributes identified by name will be indexed. * `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**). -* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*"). -* `clientRegionShort` - specifies GemFire's http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy] -with a GemFire http://geode.apache.org/releases/latest/javadoc/org/apache/geode/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 http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy] -using a GemFire http://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/org/apache/geode/cache/RegionShortcut.html[RegionShortcut] -(default is `PARTITION`). This attribute is only used when configuring server Regions, or when a p2p topology is employed. +* `poolName` - name of the dedicated Apache Geode `Pool` used to connect a client to the cluster of servers. The attribute +is only used when the application is a cache client. Defaults to `gemfirePool`. +* `regionName` - specifies the name of the Apache Geode `Region` used to store and manage `HttpSession` state +(default is "*ClusteredSpringSessions*"). +* `serverRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the server using a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/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 -the client Region is a `PROXY` or `CACHING_PROXY`. Client and server Region names are not required to match if -the client Region used to store Spring Sessions is `LOCAL`. However, keep in mind that your session state will not -be propagated to the server and you lose all the benefits of using GemFire to store and manage distributed, replicated -session state information in a cluster. +NOTE: It is important to remember that the Apache Geode 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 Sessions is `LOCAL`. However, keep in mind +that Session state will not be propagated to the server and you lose all the benefits of using Apache Geode +to store and manage distributed, replicated Session state information on the servers in a cluster. -[[httpsession-gemfire-boot-sample]] -== HttpSession with GemFire using Spring Boot Sample Application +[[spring-session-sample-boot-geode]] +== Spring Boot Sample Web Application with an Apache Geode managed HttpSession -=== Running the httpsession-gemfire-boot Sample Application +=== Running the Boot Sample Application You can run the sample by obtaining the {download-url}[source code] and invoking the following commands. First, you must run the server: ---- -$ ./gradlew :samples:httpsession-gemfire-boot:run [-Dgemfire.log-level=config] +$ ./gradlew :spring-session-sample-boot-gemfire:run [-Dgemfire.log-level=config] ---- Then, in a separate terminal, run the client: ---- -$ ./gradlew :samples:httpsession-gemfire-boot:bootRun [-Dgemfire.log-level=config] +$ ./gradlew :spring-session-sample-boot-gemfire:bootRun [-Dgemfire.log-level=config] ---- -You should now be able to access the application at http://localhost:8080/. In this sample, the web application -is the client cache and the server is standalone. +You should now be able to access the application at http://localhost:8080/. -=== Exploring the httpsession-gemfire-boot Sample Application +In this sample, the Web application is the _Spring Boot_, Apache Geode cache client +and the server is standalone, separate (JVM) process. + +=== Exploring the Boot Sample Application Try using the application. Fill out the form with the following information: @@ -207,9 +206,10 @@ along with an additional attribute (`requestCount`) indicating the number of Ses === How does it work? -We interact with the standard `HttpSession` in the the Spring MVC web service endpoint, shown here for convenience: +We interact with the standard `javax.servlet.http.HttpSession` in the the Spring Web MVC service endpoint, +shown here for convenience: -.src/main/java/sample/SessionServlet.java +.src/main/java/sample/client/Application.java [source,java] ---- @RequestMapping(method = RequestMethod.POST, path = "/session") @@ -224,24 +224,22 @@ public String session(HttpSession session, ModelMap modelMap, } ---- -Instead of using the embedded HTTP server's `HttpSession`, we are actually persisting the Session state in GemFire. -_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your session. +Instead of using the embedded HTTP server's `HttpSession`, we are actually persisting the Session state in Apache Geode. +_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your Session. Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). -NOTE: The following instructions assume you have a local GemFire installation. For more information on installation, -see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire]. +NOTE: The following instructions assume you have a local Apache Geode installation. For more information on installation, +see http://geode.apache.org/docs/guide/12/prereq_and_install.html[Prerequisites and Installation Instructions]. -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`. +If you like, you can easily remove the Session using `gfsh`. -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: +For example, on a Linux-based system type the following at the command-line: $ gfsh -Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value -of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match): +Then, enter the following commands in _Gfsh_, ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value +of your SESSION cookie, or the Session id returned by the Apache Geode OQL query (which should match): .... gfsh>connect --jmx-manager=localhost[1099] @@ -262,9 +260,10 @@ NEXT_STEP_NAME : END gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3" .... -NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh]. +NOTE: The _Apache Geode User Guide_ contains more detailed instructions on using +http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[gfsh]. -Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed. +Now visit the application at `http://localhost:8080/` again and observe the attribute we added is no longer displayed. -Alternatively, you can wait **20 seconds** for the session to expire and timeout, and then refresh the page. The attribute -we added should no longer be displayed in the table. +Alternatively, you can wait **20 seconds** for the Session to timeout and expire and then refresh the page. +The attribute we added should no longer be displayed in the table. diff --git a/docs/src/docs/asciidoc/guides/java-gemfire-clientserver.adoc b/docs/src/docs/asciidoc/guides/java-gemfire-clientserver.adoc index eec0d3c..e5296b4 100644 --- a/docs/src/docs/asciidoc/guides/java-gemfire-clientserver.adoc +++ b/docs/src/docs/asciidoc/guides/java-gemfire-clientserver.adoc @@ -1,15 +1,17 @@ -= Spring Session - HttpSession with GemFire Client/Server (Quick Start) += Spring Session - HttpSession with Apache Geode Client/Server using Java configuration John Blum :toc: -This guide describes how to configure Spring Session to transparently leverage Pivotal GemFire to back a web application's -`HttpSession` using Java Configuration. +This guide describes how to configure _Spring Session_ to transparently leverage Apache Geode to manage +a Web application's `javax.servlet.http.HttpSession` using Java Configuration. -NOTE: The completed guide can be found in the <>. +NOTE: The completed guide can be found in the +<>. == Updating Dependencies -Before using Spring Session, you must ensure that the required dependencies are included. -If you are using Maven, include the following `dependencies` in your _pom.xml_: + +Before using _Spring Session_, you must ensure that the required dependencies are included. +If you are using _Maven_, include the following `dependencies` in your `pom.xml`: .pom.xml [source,xml] @@ -20,21 +22,21 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: org.springframework.session - spring-session-data-gemfire - {spring-session-version} + spring-session-data-geode + {spring-session-data-geode-version} pom - org.springframework + org.springframework.boot spring-web - {spring-version} + ${spring-version} ---- ifeval::["{version-snapshot}" == "true"] -Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a SNAPSHOT version, we need to add the _Spring_ Snapshot Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -51,8 +53,8 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] ifeval::["{version-milestone}" == "true"] -Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a Milestone version, we need to add the _Spring_ Milestone Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -69,105 +71,102 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] // tag::config[] -[[httpsession-spring-java-configuration-gemfire-clientserver]] +[[httpsession-spring-java-configuration]] == Spring Java Configuration -After adding the required dependencies and repository declarations, we can create our Spring configuration. -The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` -with an implementation backed by Spring Session and GemFire. +After adding the required dependencies and repository declarations, we can create the _Spring_ configuration. +The _Spring_ configuration is responsible for creating a `Servlet Filter` that replaces the `HttpSession` +with an implementation backed by _Spring Session_ and Apache Geode. -Add the following Spring Configuration: +=== Client Configuration + +Add the following _Spring_ configuration: [source,java] ---- include::{samples-dir}javaconfig/gemfire-clientserver/src/main/java/sample/ClientConfig.java[tags=class] ---- -<1> The `@EnableGemFireHttpSession` annotation creates a Spring bean named `springSessionRepositoryFilter` that -implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session -and GemFire. -<2> Next, we register a `Properties` bean that allows us to configure certain aspects of the GemFire client cache -using http://gemfire.docs.pivotal.io/docs-gemfire/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 -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 -http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/client/PoolFactory.html[PoolFactory API]. -<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. +<1> First, we declare our Web application to be a Geode cache client by annotating our `ClientConfig` class +with `@ClientCacheApplication`. Additionally, we adjust a few basic, "DEFAULT" `Pool` settings. +<2> `@EnableGemFireHttpSession` creates a _Spring_ bean named `springSessionRepositoryFilter` that implements +`javax.servlet.Filter`. The filter is what replaces the `HttpSession` with an implementation provided by _Spring Session_ +and backed by Apache Geode. This will also create the necessary client-side `Region` (by default, "ClusteredSpringSessions`, +which is a `PROXY` `Region`) corresponding to the same server-side `Region` by name. All Session state will be sent +from the cache client Web application to the server through `Region` data access operations. The client-side `Region` +will use the "DEFAULT" `Pool`. +<3> Then, we adjust the port used by the cache client `Pool` to connect to the `CacheServer` +using a SDG `ClientCacheConfigurer`. -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. +TIP: In typical Geode production deployments, where the cluster includes potentially hundreds or thousands +of Geode servers (data nodes), it is more common for clients to connect to 1 or more Geode Locators running +in the cluster. A Locator passes meta-data to clients about the servers available in the cluster, the server load +and which servers have the client's data of interest, which is particularly important for direct, single-hop data access +and latency-sensitive operations. See more details about the +http://geode.apache.org/docs/guide/12/topologies_and_comm/cs_configuration/standard_client_server_deployment.html[Client/Server Deployment] +in the Apache Geode User Guide. -The `BeanPostProcessor` uses a GemFire http://geode.apache.org/releases/latest/javadoc/org/apache/geode/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. +NOTE: For more information on configuring _Spring Data Geode, refer to the +http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[Reference Guide]. -TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers), -it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data -to clients about the servers available, load and which servers have the client's data of interest, which is particularly -important for single-hop, direct data access. See more details about the http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide]. - -NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide]. - -The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both Spring Session -and GemFire out-of-the-box using the following attributes: +`@EnableGemFireHttpSession` enables a developer to configure certain aspects of both _Spring Session_ and Apache Geode +out-of-the-box using the following attributes: +* `clientRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the client with a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/client/ClientRegionShortcut.html[ClientRegionShortcut] +(default is `PROXY`). This attribute is only used when configuring a client `Region`. +* `indexableSessionAttributes` - Identifies the Session attributes by name that should be indexed for querying operations. +Only Session attributes identified by name will be indexed. * `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**). -* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*"). -* `clientRegionShort` - specifies GemFire's http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy] -with a GemFire http://geode.apache.org/releases/latest/javadoc/org/apache/geode/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 http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy] -using a GemFire http://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/org/apache/geode/cache/RegionShortcut.html[RegionShortcut] -(default is `PARTITION`). This attribute is only used when configuring server Regions, or when a p2p topology is employed. +* `poolName` - name of the dedicated Apache Geode `Pool` used to connect a client to the cluster of servers. The attribute +is only used when the application is a cache client. Defaults to `gemfirePool`. +* `regionName` - specifies the name of the Apache Geode `Region` used to store and manage `HttpSession` state +(default is "*ClusteredSpringSessions*"). +* `serverRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the server using a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/RegionShortcut.html[RegionShortcut] +(default is `PARTITION`). This attribute is only used when configuring server `Regions`, or when a P2P topology is employed. -NOTE: It is important to note that the GemFire client Region name must match a server Region by the same name if -the client Region is a `PROXY` or `CACHING_PROXY`. Names are not required to match if the client Region used to -store Spring Sessions is `LOCAL`, however, keep in mind that your session state will not be propagated to the server -and you lose all benefits of using GemFire to store and manage distributed, replicated session state information -in a cluster. - -NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when -a peer-to-peer (P2P) topology, and more specifically, a GemFire peer cache is used. +NOTE: It is important to remember that the Apache Geode 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 Sessions is `LOCAL`. However, keep in mind +that Session state will not be propagated to the server and you lose all the benefits of using Apache Geode +to store and manage distributed, replicated Session state information on the servers in a cluster. === 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. +So far, we only covered one side of the equation. We also need an Apache Geode Server for our cache client to talk to +and send Session state to the server to manage. -In this sample, we will use the following GemFire Server Java Configuration: +In this sample, we will use the following Java configuration to spin up an Apache Geode Server: [source,java] ---- include::{samples-dir}javaconfig/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_"). -We have also set the session timeout to **30 seconds**. Later, we will see how this timeout is used. -<2> Next, we configure the GemFire Server using GemFire System Properties very much like our P2P samples. -With the `mcast-port` set to 0 and no `locators` property specified, our server will be standalone. We also allow a -JMX client (e.g. _Gfsh_) to connect to our server with the use of the GemFire-specific JMX System properties. -<3> Then, we create an instance of 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. -<5> Finally, we declare a `main` method as an entry point for launching and running our GemFire Server -from the command-line. +<1> First, we use the **new** _Spring Data Geode_ configuration annotation `@CacheServerApplication` to simplify +the creation of a peer cache instance along with a `CacheServer` for cache clients to connect. +<2> (Optional) Then, the `ServerConfig` class is annotated with `@EnableGemFireHttpSession` to create the necessary +server-side `Region` (by default, "_ClusteredSpringSessions_") used to store the `HttpSessions` state. This step is +optional since the Session `Region` could be created manually, perhaps using external means. +Using `@EnableGemFireHttpSession` is convenient. +<3> Finally, we adjust the port that the `CacheServer` will use to listen for cache clients by declaring +a `CacheServerConfigurer` bean to modify the SDG `CacheServerFactoryBean` using property placeholders. + +The sample makes use of _Spring's_ `PropertySourcesPlaceholderConfigurer` in order to externalize the sample +application's configuration using a properties file or with JVM System properties, which ever. == Java Servlet Container Initialization -Our <> created a Spring bean named `springSessionRepositoryFilter` -that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` -with a custom implementation backed by Spring Session and GemFire. +Our <> created a _Spring_ bean named `springSessionRepositoryFilter` +that implements `javax.servlet.Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the +`javax.servlet.http.HttpSession` with a custom implementation backed by _Spring Session_ and Apache Geode. -In order for our `Filter` to do its magic, Spring needs to load our `ClientConfig` class. We also need to ensure our -Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. Fortunately, Spring Session -provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both of these steps extremely easy. +In order for our `Filter` to do its magic, _Spring_ needs to load the `ClientConfig` class. We also need to ensure our +Servlet container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. + +Fortunately, _Spring Session_ provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both +of these steps extremely easy. You can find an example below: @@ -177,36 +176,38 @@ You can find an example below: include::{samples-dir}javaconfig/gemfire-clientserver/src/main/java/sample/Initializer.java[tags=class] ---- -NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`. +NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend +`AbstractHttpSessionApplicationInitializer`. -<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`. -This ensures that a Spring bean named `springSessionRepositoryFilter` is registered with our Servlet Container -and used for every request. -<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow Spring to load our `ClientConfig`. +<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`. This ensures that a _Spring_ bean named +`springSessionRepositoryFilter` is registered with our Servlet container and used on every HTTP request. +<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow _Spring_ to load +our `ClientConfig`. // end::config[] -[[httpsession-gemfire-clientserver-java-sample-app]] -== HttpSession with GemFire (Client/Server) Sample Application +[[spring-session-sample-java-geode-clientserver]] +== HttpSession managed by a Java configured, Apache Geode Client/Server Sample Application - -=== Running the httpsession-gemfire-clientserver Sample Application +=== Running the Geode Sample Application You can run the sample by obtaining the {download-url}[source code] and invoking the following commands. First, you need to run the server using: ---- -$ ./gradlew :samples:httpsession-gemfire-clientserver:run [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-javaconfig-gemfire-clientserver:run [-gemfire.log-level=info] ---- -Then, in a separate terminal, you run the client using: +Then, in a separate terminal, run the client using: ---- -$ ./gradlew :samples:httpsession-gemfire-clientserver:tomcatRun [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-javaconfig-gemfire-clientserver:tomcatRun [-gemfire.log-level=info] ---- -You should now be able to access the application at http://localhost:8080/. In this sample, the web application -is the client cache and the server is standalone. +You should now be able to access the application at http://localhost:8080/. + +In this sample, the web application is the Apache Geode cache client +and the server is standalone, separate (JVM) process. === Exploring the httpsession-gemfire-clientserver Sample Application @@ -215,7 +216,7 @@ Try using the application. Fill out the form with the following information: * **Attribute Name:** _username_ * **Attribute Value:** _john_ -Now click the **Set Attribute** button. You should now see the values displayed in the table. +Now click the **Set Attribute** button. You should now see the attribute name and value displayed in the table. === How does it work? @@ -227,21 +228,23 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below: include::{samples-dir}javaconfig/gemfire-clientserver/src/main/java/sample/SessionServlet.java[tags=class] ---- -Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire. -Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Instead of using Tomcat's `HttpSession`, we are actually persisting the Session in Apache Geode. + +_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your Session. Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). -NOTE: The following instructions assume you have a local GemFire installation. For more information on installation, -see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire]. +NOTE: The following instructions assume you have a local Apache Geode installation. For more information on installation, +see http://geode.apache.org/docs/guide/12/prereq_and_install.html[Prerequisites and Installation Instructions]. -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: +If you like, you can easily remove the Session using `gfsh`. + +For example, on a Linux-based system type the following at the command-line: $ gfsh -Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value -of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match): +Then, enter the following commands in _Gfsh_, ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value +of your SESSION cookie, or the Session id returned by the Apache Geode OQL query (which should match): .... gfsh>connect --jmx-manager=localhost[1099] @@ -262,11 +265,12 @@ NEXT_STEP_NAME : END gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3" .... -NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh]. +NOTE: The _Apache Geode User Guide_ contains more detailed instructions on using +http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[gfsh]. -Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed. +Now visit the application at `http://localhost:8080/` again and observe the attribute we added is no longer displayed. -Alternatively, you can wait **30 seconds** for the session to expire and timeout, and then refresh the page. The attribute -we added should no longer be displayed in the table. However, keep in mind, that by refreshing the page, you will inadvertently -create a new (empty) session. If you run the query again, you will also see two session IDs, the new and the old, -since GemFire keeps a "tombstone" of the old session around. +Alternatively, you can wait **20 seconds** for the Session to timeout and expire and then refresh the page. +The attribute we added should no longer be displayed in the table. However, keep in mind, by refreshing the page, +you will inadvertently create a new (empty) Session. If you run the query again, you will also see two Session ids, +the new and the old, since Apache Geode keeps a "tombstone" of the old Session around. diff --git a/docs/src/docs/asciidoc/guides/java-gemfire-p2p.adoc b/docs/src/docs/asciidoc/guides/java-gemfire-p2p.adoc index 27f2612..6667ca8 100644 --- a/docs/src/docs/asciidoc/guides/java-gemfire-p2p.adoc +++ b/docs/src/docs/asciidoc/guides/java-gemfire-p2p.adoc @@ -1,15 +1,16 @@ -= Spring Session - HttpSession with GemFire P2P (Quick Start) += Spring Session - HttpSession with Apache Geode P2P using Java Configuration John Blum :toc: -This guide describes how to configure Pivotal GemFire as a provider in Spring Session to transparently back -a web application's `HttpSession` using Java Configuration. +This guide describes how to configure Apache Geode as a provider in _Spring Session_ to transparently manage +a Web application's `javax.servlet.http.HttpSession` using Java configuration. -NOTE: The completed guide can be found in the <>. +NOTE: The completed guide can be found in the <>. == Updating Dependencies -Before using Spring Session, you must ensure that the required dependencies are included. -If you are using Maven, include the following `dependencies` in your _pom.xml_: + +Before using _Spring Session_, you must ensure that the required dependencies are included. +If you are using _Maven_, include the following `dependencies` in your `pom.xml`: .pom.xml [source,xml] @@ -20,21 +21,21 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: org.springframework.session - spring-session-data-gemfire - {spring-session-version} + spring-session-data-geode + {spring-session-data-geode-version} pom - org.springframework + org.springframework.boot spring-web - {spring-version} + ${spring-version} ---- ifeval::["{version-snapshot}" == "true"] -Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a SNAPSHOT version, we need to add the _Spring_ Snapshot Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -51,8 +52,8 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] ifeval::["{version-milestone}" == "true"] -Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a Milestone version, we need to add the _Spring_ Milestone Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -72,53 +73,56 @@ endif::[] [[httpsession-spring-java-configuration-gemfire-p2p]] == Spring Java Configuration -After adding the required dependencies and repository declarations, we can create our Spring configuration. -The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` -with an implementation backed by Spring Session and GemFire. +After adding the required dependencies and repository declarations, we can create the _Spring_ configuration. -Add the following Spring Configuration: +The _Spring_ configuration is responsible for creating a `Servlet` `Filter` that replaces the `javax.servlet.http.HttpSession` +with an implementation backed by _Spring Session_ and Apache Geode. + +Add the following _Spring_ configuration: [source,java] ---- include::{samples-dir}javaconfig/gemfire-p2p/src/main/java/sample/Config.java[tags=class] ---- -<1> The `@EnableGemFireHttpSession` annotation creates a Spring bean named `springSessionRepositoryFilter` that -implements `Filter`. The filter is what replaces the `HttpSession` with an implementation backed by Spring Session. -In this instance, Spring Session is backed by GemFire. -<2> Then, we configure a GemFire peer cache using standard GemFire System properties. We give the GemFire data node -a name using the `name` property and set `mcast-port` to 0. With the absence of a `locators` property, this data node -will be a standalone server. GemFire's `log-level` is set using an application-specific System property (`sample.httpsession.gemfire.log-level`) -that a user can specify on the command-line when running this sample application using either Maven or Gradle (default is "_warning_"). -<3> Finally, we create an instance of the GemFire peer cache that embeds GemFire in the same JVM process as the running -Spring Session sample application. +<1> First, we use the **new** _Spring Data Geode_ configuration annotation `@PeerCacheApplication` to simplify +the creation of a peer cache instance. +<2> Then, the `Config` class is annotated with `@EnableGemFireHttpSession` to create the necessary server-side `Region` +(by default, "_ClusteredSpringSessions_") used to store the `HttpSessions` state. +<3> (Optionally) Finally, we annotated the `Config` class with `@EnableManager` to start an embedded Apache Geode Manager +service to allow JMX clients (e.g. Apache Geode's _Gfsh_ shell tool) to connect and inspect the server. -TIP: Additionally, we have configured this data node (server) as a GemFire Manager as well using GemFire-specific -JMX System properties that enable JMX client (e.g. _Gfsh_) to connect to this running data node. +NOTE: For more information on configuring _Spring Data Geode, refer to the +http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[Reference Guide]. -NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide]. +`@EnableGemFireHttpSession` enables a developer to configure certain aspects of both _Spring Session_ and Apache Geode +out-of-the-box using the following attributes: -The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of Spring Session -and GemFire out-of-the-box using the following attributes: - -* `maxInactiveIntervalInSeconds` - controls HttpSession idle-timeout expiration (defaults to **30 minutes**). -* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "_ClusteredSpringSessions_"). -* `serverRegionShort` - specifies GemFire http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies] -with a GemFire http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/RegionShortcut.html[RegionShortcut] -(default is `PARTITION`). - -NOTE: `clientRegionShort` is ignored in a peer cache configuration and only applies when a client-server topology, -and more specifically, a GemFire client cache is used. +* `clientRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the client with a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/client/ClientRegionShortcut.html[ClientRegionShortcut] +(default is `PROXY`). This attribute is only used when configuring a client `Region`. +* `indexableSessionAttributes` - Identifies the Session attributes by name that should be indexed for querying operations. +Only Session attributes identified by name will be indexed. +* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**). +* `poolName` - name of the dedicated Apache Geode `Pool` used to connect a client to the cluster of servers. The attribute +is only used when the application is a cache client. Defaults to `gemfirePool`. +* `regionName` - specifies the name of the Apache Geode `Region` used to store and manage `HttpSession` state +(default is "*ClusteredSpringSessions*"). +* `serverRegionShortcut` - specifies Apache Geode http://geode.apache.org/docs/guide/12/developing/region_options/region_types.html[data management policy] +on the server using a Geode http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/RegionShortcut.html[RegionShortcut] +(default is `PARTITION`). This attribute is only used when configuring server `Regions`, or when a P2P topology is employed. == Java Servlet Container Initialization -Our <> created a Spring bean named `springSessionRepositoryFilter` -that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` -with a custom implementation backed by Spring Session and GemFire. +Our <<[httpsession-spring-java-configuration-gemfire-p2p,Spring Java Configuration>> created a _Spring_ bean named +`springSessionRepositoryFilter` that implements `javasx.servlet.Filter`. The `springSessionRepositoryFilter` bean +is responsible for replacing the `javax.servlet.http.HttpSession` with a custom implementation backed by _Spring Session_ +and Apache Geode. -In order for our `Filter` to do its magic, Spring needs to load our `Config` class. We also need to ensure our -Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. Fortunately, Spring Session -provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both of these steps extremely easy. +In order for our `Filter` to do its magic, _Spring_ needs to load our `Config` class. We also need to ensure our Servlet container (i.e. Tomcat) uses our `springSessionRepositoryFilter` on every HTTP request. + +Fortunately, _Spring Session_ provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both +of these steps extremely easy. You can find an example below: @@ -128,29 +132,29 @@ You can find an example below: include::{samples-dir}javaconfig/gemfire-p2p/src/main/java/sample/Initializer.java[tags=class] ---- -NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`. +NOTE: The name of our class (`Initializer`) does not matter. What is important is that we extend +`AbstractHttpSessionApplicationInitializer`. -<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`. -This ensures that a Spring bean named `springSessionRepositoryFilter` is registered with our Servlet Container -and used for every request. -<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow Spring to load our `Config`. +<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`. This ensures that a _Spring_ bean named +`springSessionRepositoryFilter` is registered with our Servlet container and used on every HTTP request. +<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow _Spring_ to load +our `Config` class. // end::config[] -[[httpsession-gemfire-p2p-java-sample-app]] -== HttpSession with GemFire (P2P) Sample Application +[[spring-session-sample-java-gemfire-p2p]] +== HttpSession with Apache Geode (P2P) Sample Application - -=== Running the httpsession-gemfire-p2p Sample Application +=== Running the Geode P2P Java Sample Application You can run the sample by obtaining the {download-url}[source code] and invoking the following command: ---- -$ ./gradlew :samples:httpsession-gemfire-p2p:tomcatRun [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-javaconfig-gemfire-p2p:tomcatRun [-Dgemfire.log-level=info] ---- -You should now be able to access the application at http://localhost:8080/ +You should now be able to access the application at http://localhost:8080/. -=== Exploring the httpsession-gemfire-p2p Sample Application +=== Exploring the Geode P2P Java Sample Application Try using the application. Fill out the form with the following information: @@ -169,21 +173,23 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below: include::{samples-dir}javaconfig/gemfire-p2p/src/main/java/sample/SessionServlet.java[tags=class] ---- -Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire. -Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Instead of using Tomcat's `HttpSession`, we are actually persisting the Session in Apache Geode. + +_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your Session. Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). -NOTE: The following instructions assume you have a local GemFire installation. For more information on installation, -see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire]. +NOTE: The following instructions assume you have a local Apache Geode installation. For more information on installation, +see http://geode.apache.org/docs/guide/12/prereq_and_install.html[Prerequisites and Installation Instructions]. -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: +If you like, you can easily remove the Session using `gfsh`. + +For example, on a Linux-based system type the following at the command-line: $ gfsh -Then, enter the following into _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value -of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match): +Then, enter the following into _Gfsh_, ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value +of your SESSION cookie, or the Session id returned by the Apache Geode OQL query (which should match): .... gfsh>connect --jmx-manager=localhost[1099] @@ -204,6 +210,7 @@ NEXT_STEP_NAME : END gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3" .... -NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh]. +NOTE: The _Apache Geode User Guide_ contains more detailed instructions on using +http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[gfsh]. -Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed. +Now visit the application at `http://localhost:8080/` again and observe the attribute we added is no longer displayed. diff --git a/docs/src/docs/asciidoc/guides/xml-gemfire-clientserver.adoc b/docs/src/docs/asciidoc/guides/xml-gemfire-clientserver.adoc index 93a9760..b290bb4 100644 --- a/docs/src/docs/asciidoc/guides/xml-gemfire-clientserver.adoc +++ b/docs/src/docs/asciidoc/guides/xml-gemfire-clientserver.adoc @@ -1,15 +1,16 @@ -= Spring Session - HttpSession with GemFire Client/Server using XML (Quick Start) += Spring Session - HttpSession with Apache Geode Client/Server using XML Configuration John Blum :toc: -This guide describes how to configure Spring Session to transparently leverage Pivotal GemFire to back a web application's -`HttpSession` using XML Configuration. +This guide describes how to configure _Spring Session_ to transparently leverage Apache Geode to manage +a Web application's `javax.servlet.http.HttpSession` using XML Configuration. -NOTE: The completed guide can be found in the <>. +NOTE: The completed guide can be found in the <>. == Updating Dependencies -Before using Spring Session, you must ensure that the required dependencies are included. -If you are using Maven, include the following `dependencies` in your _pom.xml_: + +Before using _Spring Session_, you must ensure that the required dependencies are included. +If you are using _Maven_, include the following `dependencies` in your `pom.xml`: .pom.xml [source,xml] @@ -20,21 +21,21 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: org.springframework.session - spring-session-data-gemfire - {spring-session-version} + spring-session-data-geode + {spring-session-data-geode-version} pom - org.springframework + org.springframework.boot spring-web - {spring-version} + ${spring-version} ---- ifeval::["{version-snapshot}" == "true"] -Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a SNAPSHOT version, we need to add the _Spring_ Snapshot Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -51,8 +52,8 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] ifeval::["{version-milestone}" == "true"] -Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a Milestone version, we need to add the _Spring_ Milestone Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -69,95 +70,87 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] // tag::config[] -[[httpsession-spring-xml-configuration-gemfire-clientserver]] +[[spring-session-sample-xml-geode-clientserver]] == Spring XML Configuration -After adding the required dependencies and repository declarations, we can create our Spring configuration. -The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` -with an implementation backed by Spring Session and GemFire. +After adding the required dependencies and repository declarations, we can create the _Spring_ configuration. +The _Spring_ configuration is responsible for creating a `Servlet` `Filter` that replaces the `javax.servlet.http.HttpSession` +with an implementation backed by _Spring Session_ and Apache Geode. -Add the following Spring Configuration: +=== Client Configuration + +Add the following _Spring_ configuration: [source,xml] ---- include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/spring/session-client.xml[tags=beans] ---- -<1> Spring annotation configuration support is enabled with `` 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 -http://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 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. +<1> (Optional) First, we can include a `Properties` bean to configure certain aspects of the Apache Geode `ClientCache` +using http://geode.apache.org/docs/guide/12/reference/topics/gemfire_properties.html[GemFire Properties]. In this case, +we are just setting Apache Geode's "`log-level`" from a application-specific System property, defaulting to "`warning`" +if unspecified. +<2> We must create an instance of an Apache Geode `ClientCache` initialized with our `gemfireProperties`. +<3> Then we configure a `Pool` of client connections to talk to the Apache Geode 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. +has been configured to connect directly to the server. +<4> Finally, a `GemFireHttpSessionConfiguration` bean is registered to enable _Spring Session_ functionality. -TIP: In typical GemFire deployments, where the cluster includes potentially hundreds of GemFire data nodes (servers), -it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator passes meta-data -to clients about the servers available, load and which servers have the client's data of interest, which is particularly -important for single-hop, direct data access. See more details about the http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide]. +TIP: In typical Geode production deployments, where the cluster includes potentially hundreds or thousands +of Geode servers (data nodes), it is more common for clients to connect to 1 or more Geode Locators running +in the cluster. A Locator passes meta-data to clients about the servers available in the cluster, the server load +and which servers have the client's data of interest, which is particularly important for direct, single-hop data access +and latency-sensitive operations. See more details about the +http://geode.apache.org/docs/guide/12/topologies_and_comm/cs_configuration/standard_client_server_deployment.html[Client/Server Deployment] +in the Apache Geode User Guide. -NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide]. +NOTE: For more information on configuring _Spring Data Geode, refer to the +http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[Reference Guide]. === 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. +So far, we only covered one side of the equation. We also need an Apache Geode Server for our cache client to talk to +and send Session state to the server to manage. -In this sample, we will use the following GemFire Server Java Configuration: +In this sample, we will use the following XML configuration to spin up an Apache Geode Server: [source,xml] ---- include::{samples-dir}xml/gemfire-clientserver/src/main/resources/META-INF/spring/session-server.xml[tags=beans] ---- -<1> First, we enable Spring annotation config support with the `` element so that any -Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported -by Spring will be configured appropriately. -<2> A `PropertySourcesPlaceholderConfigurer` is registered to replace placeholders in our Spring XML configuration -meta-data with property values 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. -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. +<1> (Optional) First, we can include a `Properties` bean to configure certain aspects of the Apache Geode peer `Cache` +using http://geode.apache.org/docs/guide/12/reference/topics/gemfire_properties.html[GemFire Properties]. In this case, +we are just setting Apache Geode's "`log-level`" from a application-specific System property, defaulting to "`warning`" +if unspecified. +<2> We must configure an Apache Geode peer `Cache` instance initialized with the Apache Geode properties. +<3> Next, we define a `CacheServer` with sensible configuration for `bind-address` and `port` used by our cache client +application to connect to the server to pass Session state. +<4> Finally, we enable the same _Spring Session_ functionality we declared in the client XML configuration +by registering an instance of `GemFireHttpSessionConfiguration`, except we set the Session expiration timeout +to **30 seconds**. We explain what this means later. -The GemFire Server configuration gets bootstrapped with the following: +The Apache Geode Server gets bootstrapped with the following: [source,java] ---- -include::{samples-dir}xml/gemfire-clientserver/src/main/java/sample/Application.java[tags=class] +include::{samples-dir}xml/gemfire-clientserver/src/main/java/sample/ServerConfig.java[tags=class] ---- -TIP: Instead of a simple Java class with a main method, you could also use _Spring Boot_. +TIP: Rather than defining a simple Java class with a `main` method, you might consider using _Spring Boot_ instead. -<1> The `@Configuration` annotation designates this Java class as a source for Spring configuration meta-data using -Spring's annotation configuration support. -<2> Primarily, the configuration comes from the `META-INF/spring/session-server.xml` file, which is also the reason -why _Spring Boot_ was not used in this sample, since using XML seemingly defeats the purpose and benefits -of using Spring Boot. However, this sample is about demonstrating how to use Spring XML to configure -the GemFire client and server. +<1> The `@Configuration` annotation designates this Java class as a source of _Spring_ configuration meta-data using +7.9. Annotation-based container configuration[_Spring's_ annotation configuration support]. +<2> Primarily, the configuration comes from the `META-INF/spring/session-server.xml` file. == XML Servlet Container Initialization -Our <> created a Spring bean named `springSessionRepositoryFilter` -that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with -a custom implementation that is backed by Spring Session and GemFire. +Our <> created a _Spring_ bean named `springSessionRepositoryFilter` +that implements `javax.servlet.Filter` intervace. The `springSessionRepositoryFilter` bean is responsible for replacing +the `javax.servlet.http.HttpSession` with a custom implementation that is provided by _Spring Session_ and Apache Geode. + +In order for our `Filter` to do its magic, we need to instruct _Spring_ to load our `session-client.xml` configuration file. -In order for our `Filter` to do its magic, we need to instruct Spring to load our `session-client.xml` configuration file. We do this with the following configuration: .src/main/webapp/WEB-INF/web.xml @@ -170,7 +163,7 @@ include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/web.xml[t The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the `contextConfigLocation` context parameter value and picks up our _session-client.xml_ configuration file. -Finally, we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` +Finally, we need to ensure that our Servlet container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. The following snippet performs this last step for us: @@ -182,12 +175,12 @@ include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/web.xml[t ---- The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] -will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy` -is invoked, the `springSessionRepositoryFilter` will be invoked. +will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every HTTP request, +the `DelegatingFilterProxy` is invoked, which delegates to the `springSessionRepositoryFilter`. // end::config[] [[httpsession-gemfire-clientserver-xml-sample-app]] -== HttpSession with GemFire (Client/Server) using XML Sample Application +== HttpSession with Apache Geode (Client/Server) using XML Sample Application === Running the httpsession-gemfire-clientserver-xml Sample Application @@ -197,17 +190,19 @@ You can run the sample by obtaining the {download-url}[source code] and invoking First, you need to run the server using: ---- -$ ./gradlew :samples:httpsession-gemfire-clientserver-xml:run [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-javaconfig-gemfire-clientserver:run [-Dgemfire.log-level=info] ---- Now, in a separate terminal, you can run the client using: ---- -$ ./gradlew :samples:httpsession-gemfire-clientserver-xml:tomcatRun [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-javaconfig-gemfire-clientserver:tomcatRun [-gemfire.log-level=info] ---- -You should now be able to access the application at http://localhost:8080/. In this sample, the web application -is the client cache and the server is standalone. +You should now be able to access the application at http://localhost:8080/. + +In this sample, the web application is the Apache Geode cache client +and the server is standalone, separate (JVM) process. === Exploring the httpsession-gemfire-clientserver-xml Sample Application @@ -228,21 +223,23 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below: include::{samples-dir}xml/gemfire-clientserver/src/main/java/sample/SessionServlet.java[tags=class] ---- -Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire. -Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Instead of using Tomcat's `HttpSession`, we are actually persisting the Session in Apache Geode. + +_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your Session. Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). -NOTE: The following instructions assume you have a local GemFire installation. For more information on installation, -see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire]. +NOTE: The following instructions assume you have a local Apache Geode installation. For more information on installation, +see http://geode.apache.org/docs/guide/12/prereq_and_install.html[Prerequisites and Installation Instructions]. -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: +If you like, you can easily remove the session using `gfsh`. + +For example, on a Linux-based system type the following at the command-line: $ gfsh -Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value -of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match): +Then, enter the following commands in _Gfsh_, ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value +of your SESSION cookie, or the Session id returned by the Apache Geode OQL query (which should match): .... gfsh>connect --jmx-manager=localhost[1099] @@ -263,11 +260,12 @@ NEXT_STEP_NAME : END gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3" .... -NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh]. +NOTE: The _Apache Geode User Guide_ contains more detailed instructions on using +http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[gfsh]. -Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed. +Now visit the application at `http://localhost:8080/` again and observe the attribute we added is no longer displayed. -Alternatively, you can wait *30 seconds* for the session to timeout (i.e. expire) and refresh the page. Again, the -attribute we added should no longer be displayed in the table. However, keep in mind, that by refreshing the page, -you will inadvertently create a new (empty) session. If you run the query again, you will also see two session IDs, -the new and the old, since GemFire keeps a "tombstone" of the old session around. +Alternatively, you can wait **20 seconds** for the Session to timeout and expire and then refresh the page. +The attribute we added should no longer be displayed in the table. However, keep in mind, by refreshing the page, +you will inadvertently create a new (empty) Session. If you run the query again, you will also see two Session ids, +the new and the old, since Apache Geode keeps a "tombstone" of the old Session around. diff --git a/docs/src/docs/asciidoc/guides/xml-gemfire-p2p.adoc b/docs/src/docs/asciidoc/guides/xml-gemfire-p2p.adoc index 6ebb417..4e07e9e 100644 --- a/docs/src/docs/asciidoc/guides/xml-gemfire-p2p.adoc +++ b/docs/src/docs/asciidoc/guides/xml-gemfire-p2p.adoc @@ -1,15 +1,17 @@ -= Spring Session - HttpSession with GemFire P2P using XML (Quick Start) += Spring Session - HttpSession with Apache Geode P2P using XML Configuration John Blum :toc: -This guide describes how to configure Pivotal GemFire as a provider in Spring Session to transparently back -a web application's `HttpSession` using XML Configuration. +This guide describes how to configure Apache Geode as a provider in _Spring Session_ to transparently manage +a Web application's `javax.servlet.http.HttpSession` using XML configuration. -NOTE: The completed guide can be found in the <>. +NOTE: The completed guide can be found in the +<>. == Updating Dependencies -Before using Spring Session, you must ensure that the required dependencies are included. -If you are using Maven, include the following `dependencies` in your _pom.xml_: + +Before using _Spring Session_, you must ensure that the required dependencies are included. +If you are using _Maven_, include the following `dependencies` in your `pom.xml`: .pom.xml [source,xml] @@ -20,21 +22,21 @@ If you are using Maven, include the following `dependencies` in your _pom.xml_: org.springframework.session - spring-session-data-gemfire - {spring-session-version} + spring-session-data-geode + {spring-session-data-geode-version} pom - org.springframework + org.springframework.boot spring-web - {spring-version} + ${spring-version} ---- ifeval::["{version-snapshot}" == "true"] -Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a SNAPSHOT version, we need to add the _Spring_ Snapshot Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -51,8 +53,8 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] ifeval::["{version-milestone}" == "true"] -Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository. -If you are using Maven, include the following `repository` declaration in your _pom.xml_: +Since we are using a Milestone version, we need to add the _Spring_ Milestone Maven Repository. +If you are using _Maven_, include the following `repository` declaration in your `pom.xml`: .pom.xml [source,xml] @@ -69,14 +71,15 @@ If you are using Maven, include the following `repository` declaration in your _ endif::[] // tag::config[] -[[httpsession-spring-xml-configuration-gemfire-p2p]] +[[httpsession-spring-xml-configuration]] == Spring XML Configuration -After adding the required dependencies and repository declarations, we can create our Spring configuration. -The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` -with an implementation backed by Spring Session and GemFire. +After adding the required dependencies and repository declarations, we can create the _Spring_ configuration. -Add the following Spring Configuration: +The _Spring_ configuration is responsible for creating a `Servlet` `Filter` that replaces the `javax.servlet.http.HttpSession` +with an implementation backed by _Spring Session_ and Apache Geode. + +Add the following Spring configuration: .src/main/webapp/WEB-INF/spring/session.xml [source,xml,indent=0] @@ -84,31 +87,27 @@ Add the following Spring Configuration: include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml[tags=beans] ---- -<1> We use the combination of `` and `GemFireHttpSessionConfiguration` because Spring Session -does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]). -This creates a Spring bean with the name of `springSessionRepositoryFilter` that implements `Filter`. The filter is what -replaces the `HttpSession` with an implementation backed by Spring Session. -In this instance, Spring Session is backed by GemFire. -<2> Then, we configure a GemFire peer cache using standard GemFire System properties. We give the GemFire data node -a name using the `name` property and set `mcast-port` to 0. With the absence of a `locators` property, this data node -will be a standalone server. GemFire's `log-level` is set using an application-specific System property -(`sample.httpsession.gemfire.log-level`) that a user can specify on the command-line when running this application -using either Maven or Gradle (default is "_warning_"). -<3> Finally, we create an instance of the GemFire peer cache that embeds GemFire in the same JVM process as the running -Spring Session sample application. +<1> (Optional) First, we can include a `Properties` bean to configure certain aspects of the Apache Geode peer `Cache` +using http://geode.apache.org/docs/guide/12/reference/topics/gemfire_properties.html[GemFire Properties]. In this case, +we are just setting Apache Geode's "`log-level`" from a application-specific System property, defaulting to "`warning`" +if unspecified. +<2> We must configure an Apache Geode peer `Cache` instance initialized with the Apache Geode properties. +<3> Finally, we enable _Spring Session_ functionality by registering an instance of `GemFireHttpSessionConfiguration`. -TIP: Additionally, we have configured this data node (server) as a GemFire Manager as well using GemFire-specific -JMX System properties that enable JMX client (e.g. _Gfsh_) to connect to this running data node. +TIP: Additionally, we have configured this data node (server) as a Apache Geode Manager as well using Geode-specific +JMX properties that enable JMX client (e.g. _Gfsh_) to connect to this running server. -NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide]. +NOTE: For more information on configuring _Spring Data Geode, refer to the +http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[Reference Guide]. == XML Servlet Container Initialization -Our <> created a Spring bean named `springSessionRepositoryFilter` -that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with -a custom implementation that is backed by Spring Session and GemFire. +The <> created a _Spring_ bean named `springSessionRepositoryFilter` +that implements `javax.servlet.Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing +the `javax.servlet.http.HttpSession` with a custom implementation that is backed by _Spring Session_ and Apache Geode. + +In order for our `Filter` to do its magic, we need to instruct _Spring_ to load our `session.xml` configuration file. -In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration file. We do this with the following configuration: .src/main/webapp/WEB-INF/web.xml @@ -121,8 +120,8 @@ include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/web.xml[tags=liste The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the `contextConfigLocation` context parameter value and picks up our _session.xml_ configuration file. -Finally, we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` -for every request. +Finally, we need to ensure that our Servlet container (i.e. Tomcat) uses our `springSessionRepositoryFilter` +for every HTTP request. The following snippet performs this last step for us: @@ -133,25 +132,24 @@ include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/web.xml[tags=sprin ---- The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] -will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy` -is invoked, the `springSessionRepositoryFilter` will be invoked. +will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every HTTP request +the `DelegatingFilterProxy` is invoked, delegating to the `springSessionRepositoryFilter`. // end::config[] -[[httpsession-gemfire-p2p-xml-sample-app]] -== HttpSession with GemFire (P2P) using XML Sample Application +[[spring-session-sample-xml-gemfire-p2p]] +== HttpSession with Apache Geode (P2P) using XML Sample Application - -=== Running the httpsession-gemfire-p2p-xml Sample Application +=== Running the Geode XML Sample Application You can run the sample by obtaining the {download-url}[source code] and invoking the following command: ---- -$ ./gradlew :samples:httpsession-gemfire-p2p-xml:tomcatRun [-Dsample.httpsession.gemfire.log-level=info] +$ ./gradlew :spring-session-sample-xml-gemfire-p2p:tomcatRun [-Dgemfire.log-level=info] ---- -You should now be able to access the application at http://localhost:8080/ +You should now be able to access the application at http://localhost:8080/. -=== Exploring the httpsession-gemfire-p2p-xml Sample Application +=== Exploring the Geode XML Sample Application Try using the application. Fill out the form with the following information: @@ -170,21 +168,23 @@ We interact with the standard `HttpSession` in the `SessionServlet` shown below: include::{samples-dir}xml/gemfire-p2p/src/main/java/sample/SessionServlet.java[tags=class] ---- -Instead of using Tomcat's `HttpSession`, we are actually persisting the values in GemFire. -Spring Session creates a cookie named SESSION in your browser that contains the id of your session. +Instead of using Tomcat's `HttpSession`, we are actually persisting the Session in Apache Geode. + +_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your session. Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]). -NOTE: The following instructions assume you have a local GemFire installation. For more information on installation, -see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire]. +NOTE: The following instructions assume you have a local Apache Geode installation. For more information on installation, +see http://geode.apache.org/docs/guide/12/prereq_and_install.html[Prerequisites and Installation Instructions]. -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: +If you like, you can easily remove the session using `gfsh`. + +For example, on a Linux-based system type the following at the command-line: $ gfsh -Then, enter the following into _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value -of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match): +Then, enter the following into _Gfsh_, ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value +of your SESSION cookie, or the Session id returned by the Apache Geode OQL query (which should match): .... gfsh>connect --jmx-manager=localhost[1099] @@ -205,6 +205,7 @@ NEXT_STEP_NAME : END gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3" .... -NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh]. +NOTE: The _Apache Geode User Guide_ contains more detailed instructions on using +http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[gfsh]. -Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed. +Now visit the application at `http://localhost:8080/` again and observe the attribute we added is no longer displayed. diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index dc82cae..8b707a0 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -13,43 +13,33 @@ _Spring Session_ provides an API and implementations for managing a user's sessi _Spring Session_ provides an API and implementations for managing a user's session information. It also provides transparent integration with: -* <> - allows replacing the HttpSession in an application container (e.g. Tomcat) neutral way. +* <> - allows replacing the `javax.servlet.http.HttpSession` in an application container +(e.g. Tomcat) neutral way. Additional features include: ** **Clustered Sessions** - _Spring Session_ makes it trivial to support <> without being tied to an application container specific solution. -** **Multiple Browser Sessions** - _Spring Session_ supports <> -in a single browser instance (i.e. multiple authenticated accounts similar to Google). -** **RESTful APIs** - _Spring Session_ allows providing session ids in headers to work -with <> - -* <> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages +** **Multiple Browser Sessions** - _Spring Session_ supports managing _multiple user sessions_ in a single browser +instance (i.e. multiple authenticated accounts similar to Google). +** **REST API** - _Spring Session_ allows providing the session ID in headers to work with REST APIs. +** **WebSocket** - provides the ability to keep the `javax.servlet.http.HttpSession` alive +when receiving `WebSocket` messages [[samples]] == Samples and Guides (Start Here) -If you are looking to get started with Spring Session, the best place to start is our Sample Applications. +If you are looking to get started with _Spring Session_ right of way, the best place to start +is with our Sample Applications. .Sample Applications using Spring Boot |=== | Source | Description | Guide -| {gh-samples-url}boot/gemfire[HttpSession with GemFire] -| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a Client/Server topology. -| link:guides/boot-gemfire.html[HttpSession with GemFire Guide] - -| {gh-samples-url}boot/findbyusername[Find by Username] -| Demonstrates how to use Spring Session to find sessions by username. -| link:guides/boot-findbyusername.html[Find by Username Guide] - -| {gh-samples-url}boot/websocket[WebSockets] -| Demonstrates how to use Spring Session with WebSockets. -| link:guides/boot-websocket.html[WebSockets Guide] - -| {gh-samples-url}boot/redis-json[HttpSession with Redis JSON serialization] -| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis using JSON serialization. -| TBD +| {gh-samples-url}boot/gemfire[HttpSession with Spring Boot and Apache Geode] +| Demonstrates how to use _Spring Session_ to manage the `HttpSession` with Apache Geode in a _Spring Boot_ application +using a Client/Server topology. +| link:guides/boot-gemfire.html[HttpSession with Spring Boot and Apache Geode Guide] |=== @@ -57,13 +47,13 @@ If you are looking to get started with Spring Session, the best place to start i |=== | Source | Description | Guide -| {gh-samples-url}javaconfig/gemfire-clientserver[HttpSession with GemFire (Client/Server)] -| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a Client/Server topology. -| link:guides/java-gemfire-clientserver.html[HttpSession with GemFire (Client/Server) Guide] +| {gh-samples-url}javaconfig/gemfire-clientserver[HttpSession with Apache Geode (Client/Server)] +| Demonstrates how to use _Spring Session_ to manage the `HttpSession` with Apache Geode using a Client/Server topology. +| link:guides/java-gemfire-clientserver.html[HttpSession with Apache Geode (Client/Server) Guide] -| {gh-samples-url}javaconfig/gemfire-p2p[HttpSession with GemFire (P2P)] -| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a P2P topology. -| link:guides/java-gemfire-p2p.html[HttpSession with GemFire (P2P) Guide] +| {gh-samples-url}javaconfig/gemfire-p2p[HttpSession with Apache Geode (P2P)] +| Demonstrates how to use _Spring Session_ to manage the `HttpSession` with Apache Geode using a P2P topology. +| link:guides/java-gemfire-p2p.html[HttpSession with Apache Geode (P2P) Guide] |=== @@ -71,66 +61,67 @@ If you are looking to get started with Spring Session, the best place to start i |=== | Source | Description | Guide -| {gh-samples-url}xml/gemfire-clientserver[HttpSession with GemFire (Client/Server)] -| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a Client/Server topology. -| link:guides/xml-gemfire-clientserver.html[HttpSession with GemFire (Client/Server) Guide] +| {gh-samples-url}xml/gemfire-clientserver[HttpSession with Apache Geode (Client/Server)] +| Demonstrates how to use _Spring Session_ to manage the `HttpSession` with Apache Geode using a Client/Server topology. +| link:guides/xml-gemfire-clientserver.html[HttpSession with Apache Geode (Client/Server) Guide] -| {gh-samples-url}xml/gemfire-p2p[HttpSession with GemFire (P2P)] -| Demonstrates how to use Spring Session to replace the `HttpSession` with GemFire using a P2P topology. -| link:guides/xml-gemfire-p2p.html[HttpSession with GemFire (P2P) Guide] +| {gh-samples-url}xml/gemfire-p2p[HttpSession with Apache Geode (P2P)] +| Demonstrates how to use _Spring Session_ to manage the `HttpSession` with Apache Geode using a P2P topology. +| link:guides/xml-gemfire-p2p.html[HttpSession with Apache Geode (P2P) Guide] |=== [[httpsession]] == HttpSession Integration -_Spring Session_ provides transparent integration with `HttpSession`. This means that developers -can switch the `HttpSession` implementation out with an implementation that is backed by _Spring Session_. +_Spring Session_ provides transparent integration with `HttpSession`. This means that developers can switch +the `javax.servlet.http.HttpSession` implementation out with an implementation that is backed by _Spring Session_. [[httpsession-why]] === Why Spring Session & HttpSession? -We have already mentioned that _Spring Session_ provides transparent integration with `HttpSession`, -but what benefits do we get out of this? +We already mentioned that _Spring Session_ provides transparent integration with `HttpSession`, but what benefits +do we get out of this? * **Clustered Sessions** - _Spring Session_ makes it trivial to support <> without being tied to an application container specific solution. -* **Multiple Browser Sessions** - _Spring Session_ supports <> -in a single browser instance (i.e. multiple authenticated accounts similar to Google). -* **RESTful APIs** - _Spring Session_ allows providing session ids in headers to work -with <> +* **Multiple Browser Sessions** - _Spring Session_ supports _multiple user sessions_ in a single browser instance +(i.e. multiple authenticated accounts similar to Google). +* **RESTful APIs** - _Spring Session_ allows providing the session ID in headers to work with REST APIs. +* **WebSocket** - provides the ability to keep the `javax.servlet.http.HttpSession` alive +when receiving `WebSocket` messages [[httpsession-gemfire]] -=== HttpSession with Pivotal GemFire +=== HttpSession with Apache Geode -When https://pivotal.io/big-data/pivotal-gemfire[Pivotal GemFire] is used with _Spring Session_, a web application's -`HttpSession` can be replaced with a **clustered** implementation managed by _Pivotal GemFire_ and conveniently -accessed with _Spring Session's_ API. +When http://geode.apache.org/[Apache Geode] is used with _Spring Session_, a web application's +`javax.servlet.http.HttpSession` can be replaced with a **clustered** implementation managed by _Apache Geode_ +and conveniently accessed using _Spring Session's_ API. -The two most common topologies to manage _Spring Sessions_ using _Pivotal GemFire_ include: +The two most common topologies to manage _Spring Sessions_ using _Apache Geode_ include: * <> * <> -Additionally, _Pivotal GemFire_ supports site-to-site replication using -http://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN functionality]. -The ability to configure and use _Pivotal GemFire's_ WAN support is independent of _Spring Session_, -and is beyond the scope of this document. +Additionally, _Apache Geode_ supports site-to-site replication using +http://geode.apache.org/docs/guide/12/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN technology]. +The ability to configure and use _Apache Geode's_ WAN support is independent of _Spring Session_, +and beyond the scope of this document. -More details on _Pivotal GemFire_ WAN functionality can be found +More details on configuring _Apache Geode_ WAN functionality using _Spring Data Geode_ can be found http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:gateway[here]. [[httpsession-gemfire-clientserver]] -==== Pivotal GemFire Client-Server +==== Apache Geode Client-Server -The http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client-Server] -topology will probably be the more common configuration preference for users when using GemFire as a provider in -Spring Session since a GemFire server will have significantly different and unique JVM heap requirements when compared -to the application. Using a client-server topology enables an application to manage (e.g. replicate) application state +The http://geode.apache.org/docs/guide/12/topologies_and_comm/cs_configuration/chapter_overview.html[Client-Server] +topology will probably be the more common configuration choice among users when using Apache Geode as a provider in +_Spring Session_ since a Geode server will have significantly different and unique JVM heap requirements as compared +to the application. Using a Client-Server topology enables an application to manage (e.g. replicate) application state independently from other application processes. -In a client-server topology, an application using Spring Session will open a client cache connection to a (remote) -GemFire server cluster to manage and provide consistent access to all `HttpSession` state. +In a Client-Server topology, an application using _Spring Session_ will open 1 or more connections to a remote cluster +of Geode servers that will manage access to all `HttpSession` state. You can configure a Client-Server topology with either: @@ -138,46 +129,47 @@ You can configure a Client-Server topology with either: * <> [[httpsession-gemfire-clientserver-java]] -===== Pivotal GemFire Client-Server Java-based Configuration +===== Apache Geode Client-Server Java-based Configuration -This section describes how to use GemFire's Client-Server topology to back an `HttpSession` with Java-based configuration. +This section describes how to configure Apache Geode's Client-Server topology with Java-based configuration. -NOTE: The <> provides a working sample on how to integrate -Spring Session and GemFire to replace the HttpSession using Java configuration. You can read the basic steps for -integration below, but you are encouraged to follow along with the detailed HttpSession with GemFire (Client-Server) -Guide when integrating with your own application. +NOTE: The <> provides a working sample demonstrating how to +integrate _Spring Session_ with Apache Geode to manage the `HttpSession` using Java configuration. You can read +through the basic steps of integration below, but you are encouraged to follow along in the detailed _`HttpSession` +with Apache Geode (Client-Server) Guide_ when integrating with your own application. include::guides/java-gemfire-clientserver.adoc[tags=config,leveloffset=+3] [[http-session-gemfire-clientserver-xml]] -===== Pivotal GemFire Client-Server XML-based Configuration +===== Apache Geode Client-Server XML-based Configuration -This section describes how to use GemFire's Client-Server topology to back an `HttpSession` with XML-based configuration. +This section describes how to use Apache Geode's Client-Server topology to back an `HttpSession` +with XML-based configuration. -NOTE: The <> provides a working sample on how to -integrate Spring Session and GemFire to replace the `HttpSession` using XML configuration. You can read the basic steps -for integration below, but you are encouraged to follow along with the detailed HttpSession with GemFire (Client-Server) -using XML Guide when integrating with your own application. +NOTE: The <> provides a working sample demonstrating +how to integrate _Spring Session_ with Apache Geode to manage the `HttpSession` using XML configuration. You can read +through the basic steps of integration below, but you are encouraged to follow along in the detailed _`HttpSession` +with Apache Geode (Client-Server) using XML Guide_ when integrating with your own application. include::guides/xml-gemfire-clientserver.adoc[tags=config,leveloffset=+3] [[httpsession-gemfire-p2p]] -==== Pivotal GemFire Peer-To-Peer (P2P) +==== Apache Geode Peer-To-Peer (P2P) -Perhaps less common would be to configure the Spring Session application as a peer member in the GemFire cluster using -the http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/p2p_configuration/chapter_overview.html[Peer-To-Peer (P2P)] topology. -In this configuration, a Spring Session application would be an actual data node (server) in the GemFire cluster, +Perhaps a less common approach is to configure the _Spring Session_ application as a peer member in the Geode cluster +using the http://geode.apache.org/docs/guide/12/topologies_and_comm/p2p_configuration/chapter_overview.html[Peer-To-Peer (P2P)] topology. +In this configuration, the _Spring Session_ application would be an actual server (data node) in the Geode cluster, and **not** a cache client as before. -One advantage to this approach is the proximity of the application to the application's state (i.e. it's data). However, -there are other effective means of accomplishing similar data dependent computations, such as using GemFire's -http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/function_exec/chapter_overview.html[Function Execution]. -Any of GemFire's other http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/product_intro.html[features] -can be used when GemFire is serving as a provider in Spring Session. +One advantage to this approach is the proximity of the application to the application's state (i.e. its data). However, +there are other effective means of accomplishing similar data dependent computations, such as using Geode's +http://geode.apache.org/docs/guide/12/developing/function_exec/chapter_overview.html[Function Execution]. +Any of Geode's other http://geode.apache.org/docs/guide/12/getting_started/product_intro.html[features] can be used +when Geode is serving as a provider in _Spring Session_. -P2P is very useful for both testing purposes as well as smaller, more focused and self-contained applications, -such as those found in a microservices architecture, and will most certainly improve on your application's latency, -throughput and consistency needs. +P2P is very useful for testing purposes as well as for smaller, more focused and self-contained applications, +such as those found in a microservices architecture, and will most certainly improve on your application's perceived +latency, throughput and consistency needs. You can configure a Peer-To-Peer (P2P) topology with either: @@ -185,39 +177,44 @@ You can configure a Peer-To-Peer (P2P) topology with either: * <> [[httpsession-gemfire-p2p-java]] -===== Pivotal GemFire Peer-To-Peer (P2P) Java-based Configuration +===== Apache Geode Peer-To-Peer (P2P) Java-based Configuration -This section describes how to use GemFire's Peer-To-Peer (P2P) topology to back an `HttpSession` using Java-based configuration. +This section describes how to configure Apache Geode's Peer-To-Peer (P2P) topology to manage an `HttpSession` +using Java-based configuration. -NOTE: The <> provides a working sample on how to integrate -Spring Session and GemFire to replace the `HttpSession` using Java configuration. You can read the basic steps -for integration below, but you are encouraged to follow along with the detailed HttpSession with GemFire (P2P) Guide -when integrating with your own application. +NOTE: The <> provides a working sample demonstrating how to +integrate _Spring Session_ with Apache Geode to manage the `HttpSession` using Java configuration. You can read +through the basic steps of integration below, but you are encouraged to follow along in the detailed _`HttpSession` +with Apache Geode (P2P) Guide_ when integrating with your own application. include::guides/java-gemfire-p2p.adoc[tags=config,leveloffset=+3] [[httpsession-gemfire-p2p-xml]] -===== Pivotal GemFire Peer-To-Peer (P2P) XML-based Configuration +===== Apache Geode Peer-To-Peer (P2P) XML-based Configuration -This section describes how to use GemFire's Peer-To-Peer (P2P) topology to back an `HttpSession` using XML-based configuration. +This section describes how to use Apache Geode's Peer-To-Peer (P2P) topology to back an `HttpSession` +using XML-based configuration. -NOTE: The <> provides a working sample on how to integrate -Spring Session and GemFire to replace the `HttpSession` using XML configuration. You can read the basic steps for -integration below, but you are encouraged to follow along with the detailed HttpSession with GemFire (P2P) using XML -Guide when integrating with your own application. +NOTE: The <> provides a working sample demonstrating how to +integrate _Spring Session_ with Apache Geode to manage the `HttpSession` using XML configuration. You can read +through the basic steps of integration below, but you are encouraged to follow along in the detailed _`HttpSession` +with Apache Geode (P2P) using XML Guide_ when integrating with your own application. include::guides/xml-gemfire-p2p.adoc[tags=config,leveloffset=+3] [[httpsession-how]] === How HttpSession Integration Works -Fortunately both `HttpSession` and `HttpServletRequest` (the API for obtaining an `HttpSession`) are both interfaces. -This means that we can provide our own implementations for each of these APIs. +Fortunately both `javax.servlet.http.HttpSession` and `javax.servlet.http.HttpServletRequest` (the API for +obtaining an `HttpSession`) are both interfaces. This means that we can provide our own implementations +for each of these APIs. -NOTE: This section describes how Spring Session provides transparent integration with `HttpSession`. The intent is so that user's can understand what is happening under the covers. This functionality is already integrated and you do NOT need to implement this logic yourself. +NOTE: This section describes how _Spring Session_ provides transparent integration with `javax.servlet.http.HttpSession`. +The intent is so users understand what is happening under the hood. This functionality is already integrated +and you do NOT need to implement this logic yourself. -First we create a custom `HttpServletRequest` that returns a custom implementation of `HttpSession`. -It looks something like the following: +First, we create a custom `javax.servlet.http.HttpServletRequest` that returns a custom implementation of +`javax.servlet.http.HttpSession`. It looks something like the following: [source, java] ---- @@ -239,10 +236,11 @@ public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { } ---- -Any method that returns an `HttpSession` is overridden. -All other methods are implemented by `HttpServletRequestWrapper` and simply delegate to the original `HttpServletRequest` implementation. +Any method that returns an `javax.servlet.http.HttpSession` is overridden. All other methods are implemented by +`javax.servlet.http.HttpServletRequestWrapper` and simply delegate to the original `javax.servlet.http.HttpServletRequest` +implementation. -We replace the `HttpServletRequest` implementation using a servlet `Filter` called `SessionRepositoryFilter`. +We replace the `javax.servlet.http.HttpServletRequest` implementation using a Servlet `Filter` called `SessionRepositoryFilter`. The pseudocode can be found below: [source, java] @@ -250,9 +248,10 @@ The pseudocode can be found below: public class SessionRepositoryFilter implements Filter { public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + HttpServletRequest httpRequest = (HttpServletRequest) request; - SessionRepositoryRequestWrapper customRequest = - new SessionRepositoryRequestWrapper(httpRequest); + + SessionRepositoryRequestWrapper customRequest = new SessionRepositoryRequestWrapper(httpRequest); chain.doFilter(customRequest, response, chain); } @@ -261,87 +260,107 @@ public class SessionRepositoryFilter implements Filter { } ---- -By passing in a custom `HttpServletRequest` implementation into the `FilterChain` we ensure that anything invoked after our `Filter` uses the custom `HttpSession` implementation. -This highlights why it is important that Spring Session's `SessionRepositoryFilter` must be placed before anything that interacts with the `HttpSession`. +By passing in a custom `javax.servlet.http.HttpServletRequest` implementation into the `FilterChain` we ensure that +anything invoked after our `Filter` uses the custom `javax.servlet.http.HttpSession` implementation. + +This highlights why it is important that _Spring Session's_ `SessionRepositoryFilter` must be placed before anything +that interacts with the `javax.servlet.http.HttpSession`. [[httpsession-httpsessionlistener]] === HttpSessionListener -Spring Session supports `HttpSessionListener` by translating `SessionDestroyedEvent` and `SessionCreatedEvent` into `HttpSessionEvent` by declaring `SessionEventHttpSessionListenerAdapter`. +_Spring Session_ supports `HttpSessionListener` by translating `SessionDestroyedEvent` and `SessionCreatedEvent` into +`HttpSessionEvent` by declaring `SessionEventHttpSessionListenerAdapter`. + To use this support, you need to: * Ensure your `SessionRepository` implementation supports and is configured to fire `SessionDestroyedEvent` and `SessionCreatedEvent`. -* Configure `SessionEventHttpSessionListenerAdapter` as a Spring bean. +* Configure `SessionEventHttpSessionListenerAdapter` as a _Spring_ bean. * Inject every `HttpSessionListener` into the `SessionEventHttpSessionListenerAdapter` -If you are using the configuration support documented in <>, then all you need to do is register every `HttpSessionListener` as a bean. -For example, assume you want to support Spring Security's concurrency control and need to use `HttpSessionEventPublisher` you can simply add `HttpSessionEventPublisher` as a bean. +If you are using the configuration support documented in <>, +then all you need to do is register every `HttpSessionListener` as a bean. + +For example, assume you want to support _Spring Security's_ concurrency control and need to use `HttpSessionEventPublisher` +you can simply add `HttpSessionEventPublisher` as a bean. [[api-session]] === Session -A `Session` is a simplified `Map` of key value pairs. - -[[api-expiringsession]] -=== ExpiringSession - -An `ExpiringSession` extends a `Session` by providing attributes related to the `Session` instance's expiration. -If there is no need to interact with the expiration information, prefer using the more simple `Session` API. +A `Session` is a simplified `Map` of key/value pairs with support for expiration. [[api-sessionrepository]] === SessionRepository A `SessionRepository` is in charge of creating, retrieving, and persisting `Session` instances. -If possible, developers should not interact directly with a `SessionRepository` or a `Session`. -Instead, developers should prefer interacting with `SessionRepository` and `Session` indirectly through the <> and <> integration. +If possible, developers should not interact directly with a `SessionRepository` or a `Session`. Instead, developers +should prefer to interact with `SessionRepository` and `Session` indirectly through the `javax.servlet.http.HttpSession` +and `WebSocket` integration. [[api-findbyindexnamesessionrepository]] === FindByIndexNameSessionRepository -Spring Session's most basic API for using a `Session` is the `SessionRepository`. -This API is intentionally very simple, so that it is easy to provide additional implementations with basic functionality. +_Spring Session's_ most basic API for using a `Session` is the `SessionRepository`. The API is intentionally +very simple so that it is easy to provide additional implementations with basic functionality. Some `SessionRepository` implementations may choose to implement `FindByIndexNameSessionRepository` also. -For example, Spring's Redis support implements `FindByIndexNameSessionRepository`. +For example, _Spring Session's Apache Geode_ support implements `FindByIndexNameSessionRepository`. -The `FindByIndexNameSessionRepository` adds a single method to look up all the sessions for a particular user. -This is done by ensuring that the session attribute with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username. -It is the responsibility of the developer to ensure the attribute is populated since Spring Session is not aware of the authentication mechanism being used. +The `FindByIndexNameSessionRepository` provides a single method to look up all the `Sessions` for a particular user. +This is done by ensuring that the session attribute with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` +is populated with the username. It is the responsibility of the developer to ensure the attribute is populated +since _Spring Session_ is not aware of the authentication mechanism being used. [NOTE] ==== Some implementations of `FindByIndexNameSessionRepository` will provide hooks to automatically index other session attributes. -For example, many implementations will automatically ensure the current Spring Security user name is indexed with the index name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`. +For example, many implementations will automatically ensure the current _Spring Security_ user name is indexed with +the index name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`. ==== [[api-enablespringhttpsession]] === EnableSpringHttpSession -The `@EnableSpringHttpSession` annotation can be added to an `@Configuration` class to expose the `SessionRepositoryFilter` as a bean named "springSessionRepositoryFilter". +The `@EnableSpringHttpSession` annotation can be added to any `@Configuration` class to expose the `SessionRepositoryFilter` +as a bean in the _Spring_ application context named "springSessionRepositoryFilter". + In order to leverage the annotation, a single `SessionRepository` bean must be provided. -It is important to note that no infrastructure for session expirations is configured for you out of the box. -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-enablegemfirehttpsession]] +=== EnableGemFireHttpSession + +The `@EnableGemFireHttpSession` annotation can be added to any `@Configuration` class in place of +the `@EnableSpringHttpSession` annotation to expose the `SessionRepositoryFilter` as a bean +in the _Spring_ application context named "springSessionRepositoryFilter" and to position Apache Geode as a provider +to manage the `javax.servlet.http.HttpSession`. + +When using the `@EnableGemFireHttpSession` annotation, additional configuration is imported out-of-the-box +that also provides a Geode specific implementation of the `SessionRepository` interface, the `GemFireOperationsSessionRepository`. [[api-gemfireoperationssessionrepository]] === GemFireOperationsSessionRepository -`GemFireOperationsSessionRepository` is a `SessionRepository` that is implemented using Spring Data's `GemFireOperationsSessionRepository`. -In a web environment, this is typically used in combination with `SessionRepositoryFilter`. -The implementation supports `SessionDestroyedEvent` and `SessionCreatedEvent` through `SessionMessageListener`. +`GemFireOperationsSessionRepository` is a `SessionRepository` implementation that is implemented using _Spring Session Data Geode's_ +`GemFireOperationsSessionRepository`. + +In a web environment, this repository is used in conjunction with the `SessionRepositoryFilter`. + +This implementation supports `SessionCreatedEvents`, `SessionDeletedEvents` and `SessionDestroyedEvents` +through `SessionEventHttpSessionListenerAdapter`. [[api-gemfireoperationssessionrepository-indexing]] -==== Using Indexes with GemFire +==== Using Indexes with Apache Geode -While best practices concerning the proper definition of indexes that positively impact GemFire's performance is beyond -the scope of this document, it is important to realize that Spring Session Data GemFire creates and uses indexes to +While best practices concerning the proper definition of Indexes that positively impact Geode's performance is beyond +the scope of this document, it is important to realize that _Spring Session Data Geode_ creates and uses Indexes to query and find Sessions efficiently. -Out-of-the-box, Spring Session Data GemFire creates 1 Hash-typed Index on the principal name. There are two different buit in -strategies for finding the principal name. The first strategy is that the value of the session attribute with the name -`FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` will be indexed to the same index name. For example: +Out-of-the-box, _Spring Session Data Geode_ creates 1 Hash-typed Index on the principal name. There are two different +built-in strategies for finding the principal name. The first strategy is that the value of the Session attribute +with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` will be Indexed to the same index name. + +For example: [source,java,indent=0] ---- @@ -350,11 +369,12 @@ include::{docs-itest-dir}docs/gemfire/indexing/HttpSessionGemFireIndexingIntegra ---- [[api-gemfireoperationssessionrepository-indexing-security]] -==== Using Indexes with GemFire & Spring Security +==== Using Indexes with Apache Geode & Spring Security -Alternatively, Spring Session Data GemFire will map Spring Security's current `Authentication#getName()` to the index -`FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`. For example, if you are using Spring Security you can -find the current user's sessions using: +Alternatively, _Spring Session Data Geode_ will map _Spring Security's_ current `Authentication#getName()` to the Index +`FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`. + +For example, if you are using _Spring Security_ you can find the current user's sessions using: [source,java,indent=0] ---- @@ -368,13 +388,13 @@ include::{docs-itest-dir}docs/gemfire/indexing/HttpSessionGemFireIndexingIntegra This enables developers using the `GemFireOperationsSessionRepository` programmatically to query and find all Sessions with a given principal name efficiently. -Additionally, Spring Session Data GemFire will create a Range-based Index on the implementing Session's Map-type +Additionally, _Spring Session Data Geode_ will create a Range-based Index on the implementing Session's Map-type `attributes` property (i.e. on any arbitrary Session attribute) when a developer identifies 1 or more named Session -attributes that should be indexed by GemFire. +attributes that should be indexed by Geode. Sessions attributes to index can be specified with the `indexableSessionAttributes` attribute on the `@EnableGemFireHttpSession` -annotation. A developer adds this annotation to their Spring application `@Configuration` class when s/he wishes to -enable Spring Session support for HttpSession backed by GemFire. +annotation. A developer adds this annotation to their _Spring_ application `@Configuration` class when s/he wishes to +enable _Spring Session's_ support for `HttpSession` backed by Apache Geode. [source,java,indent=0] ---- @@ -406,17 +426,17 @@ Please find additional information below. === Support You can get help by asking questions on http://stackoverflow.com/questions/tagged/spring-session[StackOverflow with the tag spring-session]. -Similarly we encourage helping others by answering questions on StackOverflow. +Similarly we encourage helping others by answering questions on _StackOverflow_. [[community-source]] === Source Code -Our source code can be found on github at https://github.com/spring-projects/spring-session/ +The source code can be found on GitHub at https://github.com/spring-projects/spring-session-data-geode [[community-issues]] === Issue Tracking -We track issues in github issues at https://github.com/spring-projects/spring-session/issues +We track issues in GitHub Issues at https://github.com/spring-projects/spring-session-data-geode/issues [[community-contributing]] === Contributing @@ -426,26 +446,21 @@ We appreciate https://help.github.com/articles/using-pull-requests/[Pull Request [[community-license]] === License -Spring Session is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license]. - -[[community-extensions]] -=== Community Extensions -https://github.com/maseev/spring-session-orientdb[Spring Session OrientDB] -http://infinispan.org/docs/dev/user_guide/user_guide.html#externalizing_session_using_spring_session[Spring Session Infinispan] +_Spring Session Data Geode_ and _Spring Session Data GemFire_ are Open Source Software released under the +http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license]. [[minimum-requirements]] == Minimum Requirements -The minimum requirements for Spring Session are: +The minimum requirements for _Spring Session_ are: -* Java 5+ -* If you are running in a Servlet Container (not required), Servlet 2.5+ -* If you are using other Spring libraries (not required), the minimum required version is Spring 3.2.14. -While we re-run all unit tests against Spring 3.2.x, we recommend using the latest Spring 4.x version when possible. -* `@EnableRedisHttpSession` requires Redis 2.8+. This is necessary to support <> +* Java 8+ +* If you are running in a Servlet container (not required), Servlet 2.5+ +* If you are using other _Spring_ libraries (not required), the minimum required version is _Spring Framework_ 5.0.0.RC2. +* `@EnableGemFireHttpSession` requires _Spring Data Geode_ 2.0.0.RC2 and _Spring Data GemFire_ 2.0.0.RC2. +* `@EnableGemFireHttpSession` requires Apache Geode 1.2.0 or Pivotal GemFire 9.1.0. [NOTE] ==== -At its core Spring Session only has a required dependency on `spring-jcl`. -For an example of using Spring Session without any other Spring dependencies, refer to the <> application. +At its core _Spring Session_ only has a required dependency on `spring-jcl`. ==== diff --git a/samples/boot/gemfire/spring-session-sample-boot-gemfire.gradle b/samples/boot/gemfire/spring-session-sample-boot-gemfire.gradle index abc7185..5adaa6e 100644 --- a/samples/boot/gemfire/spring-session-sample-boot-gemfire.gradle +++ b/samples/boot/gemfire/spring-session-sample-boot-gemfire.gradle @@ -19,11 +19,6 @@ dependencies { testCompile "org.springframework.boot:spring-boot-starter-test" testCompile seleniumDependencies -// testCompile 'org.seleniumhq.selenium:htmlunit-driver' -// testCompile 'org.seleniumhq.selenium:selenium-api' -// testCompile 'org.seleniumhq.selenium:selenium-java' -// testCompile 'org.seleniumhq.selenium:selenium-remote-driver' -// testCompile 'org.seleniumhq.selenium:selenium-support' integrationTestCompile seleniumDependencies @@ -45,7 +40,7 @@ task runGemFireServer() { doLast { ext.port = reservePort() - println "Starting GemFire Server on port [$port] ..." + println "Starting Apache Geode Server on port [$port] ..." def out = new StringBuilder() def err = new StringBuilder() @@ -56,9 +51,7 @@ task runGemFireServer() { 'java', '-server', '-ea', '-classpath', classpath, //"-Dgemfire.log-file=gemfire-server.log", //"-Dgemfire.log-level=config", - "-Dspring-session-data-gemfire.cache.server.port=$port", - "-Dspring-session-data-gemfire.log.level=" - + System.getProperty('spring-session-data-gemfire.log.level', 'warning'), + "-Dspring.session.data.geode.cache.server.port=$port", 'sample.server.GemFireServer' ] @@ -83,11 +76,10 @@ integrationTest { systemProperties['server.port'] = port //systemProperties['gemfire.log-file'] = "gemfire-client.log" //systemProperties['gemfire.log-level'] = "config" - systemProperties['spring-session-data-gemfire.cache.server.port'] = runGemFireServer.port - systemProperties['spring-session-data-gemfire.log.level'] = System.getProperty("spring-session-data-gemfire.log.level", "warning") + systemProperties['spring.session.data.geode.cache.server.port'] = runGemFireServer.port } doLast { - println 'Stopping GemFire Server...' + println 'Stopping Apache Geode Server...' runGemFireServer.process?.destroyForcibly() } } diff --git a/samples/boot/gemfire/src/main/java/sample/client/Application.java b/samples/boot/gemfire/src/main/java/sample/client/Application.java index 6f2c9ad..a1ded2c 100644 --- a/samples/boot/gemfire/src/main/java/sample/client/Application.java +++ b/samples/boot/gemfire/src/main/java/sample/client/Application.java @@ -16,9 +16,6 @@ package sample.client; -import static java.util.Arrays.stream; -import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; - import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; @@ -26,44 +23,21 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import javax.servlet.http.HttpSession; -import org.apache.geode.cache.Region; -import org.apache.geode.cache.client.ClientCache; -import org.apache.geode.cache.client.ClientRegionShortcut; -import org.apache.geode.cache.client.Pool; -import org.apache.geode.cache.client.PoolManager; -import org.apache.geode.cache.client.internal.PoolImpl; -import org.apache.geode.management.membership.ClientMembership; -import org.apache.geode.management.membership.ClientMembershipEvent; -import org.apache.geode.management.membership.ClientMembershipListenerAdapter; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.data.gemfire.client.ClientCacheFactoryBean; -import org.springframework.data.gemfire.client.ClientRegionFactoryBean; -import org.springframework.data.gemfire.config.xml.GemfireConstants; -import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; -import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; @@ -88,18 +62,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @since 1.2.1 */ // tag::class[] -@SpringBootApplication -@Controller -@EnableGemFireHttpSession(poolName = "DEFAULT")// <1> -@SuppressWarnings("unused") +@SpringBootApplication // <1> +@Controller // <2> public class Application { - static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(30); - - static final CountDownLatch LATCH = new CountDownLatch(1); - - static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config"; - static final String GEMFIRE_DEFAULT_POOL_NAME = "DEFAULT"; static final String INDEX_TEMPLATE_VIEW_NAME = "index"; static final String PING_RESPONSE = "PONG"; static final String REQUEST_COUNT_ATTRIBUTE_NAME = "requestCount"; @@ -108,197 +74,32 @@ public class Application { SpringApplication.run(Application.class, args); } - @Configuration - static class GemFireConfiguration { + @ClientCacheApplication(name = "SpringSessionDataGeodeClientBootSample", logLevel = "warning", + pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true) // <3> + @EnableGemFireHttpSession(poolName = "DEFAULT") // <4> + static class ClientCacheConfiguration extends IntegrationTestConfiguration { + // Required to resolve property placeholders in Spring @Value annotations. @Bean static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { // <2> - - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", applicationName()); - //gemfireProperties.setProperty("log-file", "gemfire-client.log"); - gemfireProperties.setProperty("log-level", logLevel()); - - return gemfireProperties; - } - - String applicationName() { - return "spring-session-data-gemfire-boot-sample.".concat(getClass().getSimpleName()); - } - - String logLevel() { - return System.getProperty("spring-session-data-gemfire.log.level", DEFAULT_GEMFIRE_LOG_LEVEL); - } - @Bean - ClientCacheFactoryBean gemfireCache( - @Value("${spring-session-data-gemfire.cache.server.host:localhost}") String host, - @Value("${spring-session-data-gemfire.cache.server.port:40404}") int port) { // <3> + ClientCacheConfigurer clientCacheServerPortConfigurer( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <5> - ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setProperties(gemfireProperties()); - - // GemFire Pool settings <4> - gemfireCache.setKeepAlive(false); - gemfireCache.setPingInterval(TimeUnit.SECONDS.toMillis(5)); - gemfireCache.setReadTimeout(intValue(TimeUnit.SECONDS.toMillis(15))); - gemfireCache.setRetryAttempts(1); - gemfireCache.setSubscriptionEnabled(true); - gemfireCache.setThreadLocalConnections(false); - gemfireCache.setServers(Collections.singletonList(newConnectionEndpoint(host, port))); - - registerClientMembershipListener(); // <5> - - return gemfireCache; - } - - int intValue(Number number) { - return number.intValue(); - } - - ConnectionEndpoint newConnectionEndpoint(String host, int port) { - return new ConnectionEndpoint(host, port); - } - - void registerClientMembershipListener() { - - ClientMembership.registerClientMembershipListener(new ClientMembershipListenerAdapter() { - - @Override - public void memberJoined(ClientMembershipEvent event) { - LATCH.countDown(); - } - }); - } - - @Bean(name = "Example") - @Profile("debug") - ClientRegionFactoryBean exampleRegion(ClientCache gemfireCache) { - - ClientRegionFactoryBean exampleRegionFactory = new ClientRegionFactoryBean<>(); - - exampleRegionFactory.setCache(gemfireCache); - exampleRegionFactory.setClose(false); - exampleRegionFactory.setShortcut(ClientRegionShortcut.PROXY); - - return exampleRegionFactory; - } - - @Bean - BeanPostProcessor gemfireClientServerReadyBeanPostProcessor( - @Value("${spring-session-data-gemfire.cache.server.host:localhost}") final String host, - @Value("${spring-session-data-gemfire.cache.server.port:40404}") final int port) { // <5> - - return new BeanPostProcessor() { - - private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true); - private final AtomicReference defaultPool = new AtomicReference<>(null); - - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - - if (shouldCheckWhetherGemFireServerIsRunning(bean, beanName)) { - try { - validateCacheClientNotified(); - validateCacheClientSubscriptionQueueConnectionEstablished(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - return bean; - } - - private boolean shouldCheckWhetherGemFireServerIsRunning(Object bean, String beanName) { - - return (isGemFireRegion(bean, beanName) - ? checkGemFireServerIsRunning.compareAndSet(true, false) - : whenGemFireCache(bean, beanName)); - } - - private boolean isGemFireRegion(Object bean, String beanName) { - - return (GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME.equals(beanName) - || bean instanceof Region); - } - - private boolean whenGemFireCache(Object bean, String beanName) { - - if (bean instanceof ClientCache) { - defaultPool.compareAndSet(null, ((ClientCache) bean).getDefaultPool()); - } - - return false; - } - - private void validateCacheClientNotified() throws InterruptedException { - - boolean didNotTimeout = LATCH.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - - Assert.state(didNotTimeout, String.format( - "GemFire Cache Server failed to start on host [%s] and port [%d]", host, port)); - } - - @SuppressWarnings("all") - private void validateCacheClientSubscriptionQueueConnectionEstablished() throws InterruptedException { - - boolean cacheClientSubscriptionQueueConnectionEstablished = false; - - Pool pool = defaultIfNull(this.defaultPool.get(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, - GEMFIRE_DEFAULT_POOL_NAME); - - if (pool instanceof PoolImpl) { - - long timeout = (System.currentTimeMillis() + DEFAULT_TIMEOUT); - - while (System.currentTimeMillis() < timeout - && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { - - synchronized (pool) { - TimeUnit.MILLISECONDS.timedWait(pool, 500L); - } - - } - - cacheClientSubscriptionQueueConnectionEstablished |= - ((PoolImpl) pool).isPrimaryUpdaterAlive(); - } - - Assert.state(cacheClientSubscriptionQueueConnectionEstablished, String.format( - "Cache client subscription queue connection not established; GemFire Pool was [%s];" - + " GemFire Pool configuration was [locators = %s, servers = %s]", - pool, pool.getLocators(), pool.getServers())); - } - - private Pool defaultIfNull(Pool pool, String... poolNames) { - - AtomicReference poolRef = new AtomicReference<>(null); - - stream(nullSafeArray(poolNames, String.class)).forEach(poolName -> - poolRef.set(Optional.ofNullable(pool).orElseGet(() -> PoolManager.find(poolName)))); - - return poolRef.get(); - } - - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - }; + return (beanName, cacheServerFactoryBean) -> + cacheServerFactoryBean.setServers(Collections.singletonList( + newConnectionEndpoint("localhost", port))); } } @Configuration - static class SpringWebMvcConfiguration { + static class SpringWebMvcConfiguration { // <6> @Bean - public WebMvcConfigurer webMvcConfig() { // <6> + public WebMvcConfigurer webMvcConfig() { return new WebMvcConfigurer() { @@ -310,9 +111,6 @@ public class Application { } } - @Autowired(required = false) - private ClientCache gemfireCache; - @ExceptionHandler @ResponseBody public String errorHandler(Throwable error) { @@ -323,35 +121,22 @@ public class Application { @RequestMapping(method = RequestMethod.GET, path = "/ping") @ResponseBody - public String ping() { // <7> + public String ping() { return PING_RESPONSE; } - @RequestMapping(method = RequestMethod.GET, path = "/time") - @ResponseBody - public String time() { - return String.valueOf(this.gemfireCache.getRegion("/Example").get("time")); - } - - /* - @RequestMapping("/") - public String index() { // <6> - return INDEX_TEMPLATE_VIEW_NAME; - } - */ - @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) { // <7> - modelMap.addAttribute("sessionAttributes", attributes(setAttribute(updateRequestCount(session), name, value))); + modelMap.addAttribute("sessionAttributes", + attributes(setAttribute(updateRequestCount(session), name, value))); return INDEX_TEMPLATE_VIEW_NAME; } - // end::class[] +// end::class[] - /* (non-Javadoc) */ @SuppressWarnings("all") HttpSession updateRequestCount(HttpSession session) { @@ -362,17 +147,14 @@ public class Application { } } - /* (non-Javadoc) */ Integer nullSafeIncrement(Integer value) { return (nullSafeIntValue(value) + 1); } - /* (non-Javadoc) */ int nullSafeIntValue(Number value) { return Optional.ofNullable(value).map(Number::intValue).orElse(0); } - /* (non-Javadoc) */ HttpSession setAttribute(HttpSession session, String attributeName, String attributeValue) { if (isSet(attributeName, attributeValue)) { @@ -382,7 +164,6 @@ public class Application { return session; } - /* (non-Javadoc) */ boolean isSet(String... values) { boolean set = true; @@ -394,7 +175,6 @@ public class Application { return set; } - /* (non-Javadoc) */ Map attributes(HttpSession session) { Map sessionAttributes = new HashMap<>(); @@ -406,7 +186,6 @@ public class Application { return sessionAttributes; } - /* (non-Javadoc) */ Iterable toIterable(Enumeration enumeration) { return () -> Optional.ofNullable(enumeration).map(CollectionUtils::toIterator) diff --git a/samples/boot/gemfire/src/main/java/sample/client/IntegrationTestConfiguration.java b/samples/boot/gemfire/src/main/java/sample/client/IntegrationTestConfiguration.java new file mode 100644 index 0000000..2158a15 --- /dev/null +++ b/samples/boot/gemfire/src/main/java/sample/client/IntegrationTestConfiguration.java @@ -0,0 +1,168 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sample.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.Pool; +import org.apache.geode.cache.client.PoolManager; +import org.apache.geode.cache.client.internal.PoolImpl; +import org.apache.geode.management.membership.ClientMembership; +import org.apache.geode.management.membership.ClientMembershipEvent; +import org.apache.geode.management.membership.ClientMembershipListenerAdapter; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; +import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.util.Assert; + +/** + * The IntegrationTestConfiguration class... + * + * @author John Blum + * @since 1.0.0 + */ +public class IntegrationTestConfiguration { + + static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(60); + + static final CountDownLatch LATCH = new CountDownLatch(1); + + static final String GEMFIRE_DEFAULT_POOL_NAME = "DEFAULT"; + + @Bean + BeanPostProcessor clientServerReadyBeanPostProcessor( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <5> + + return new BeanPostProcessor() { + + private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true); + private final AtomicReference defaultPool = new AtomicReference<>(null); + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + + if (shouldCheckWhetherGemFireServerIsRunning(bean, beanName)) { + try { + validateCacheClientNotified(); + validateCacheClientSubscriptionQueueConnectionEstablished(); + } + catch (InterruptedException cause) { + Thread.currentThread().interrupt(); + } + } + + return bean; + } + + private boolean shouldCheckWhetherGemFireServerIsRunning(Object bean, String beanName) { + + return (isGemFireRegion(bean, beanName) + ? checkGemFireServerIsRunning.compareAndSet(true, false) + : whenGemFireCache(bean, beanName)); + } + + private boolean isGemFireRegion(Object bean, String beanName) { + + return (GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME.equals(beanName) + || bean instanceof Region); + } + + private boolean whenGemFireCache(Object bean, String beanName) { + + if (bean instanceof ClientCache) { + defaultPool.compareAndSet(null, ((ClientCache) bean).getDefaultPool()); + } + + return false; + } + + private void validateCacheClientNotified() throws InterruptedException { + + boolean didNotTimeout = LATCH.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + + Assert.state(didNotTimeout, String.format( + "Apache Geode Cache Server failed to start on host [%s] and port [%d]", "localhost", port)); + } + + @SuppressWarnings("all") + private void validateCacheClientSubscriptionQueueConnectionEstablished() throws InterruptedException { + + boolean cacheClientSubscriptionQueueConnectionEstablished = false; + + Pool pool = defaultIfNull(this.defaultPool.get(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, + GEMFIRE_DEFAULT_POOL_NAME); + + if (pool instanceof PoolImpl) { + + long timeout = (System.currentTimeMillis() + DEFAULT_TIMEOUT); + + while (System.currentTimeMillis() < timeout && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { + synchronized (pool) { + TimeUnit.MILLISECONDS.timedWait(pool, 500L); + } + + } + + cacheClientSubscriptionQueueConnectionEstablished |= ((PoolImpl) pool).isPrimaryUpdaterAlive(); + } + + Assert.state(cacheClientSubscriptionQueueConnectionEstablished, + String.format("Cache client subscription queue connection not established; Geode Pool was [%s];" + + " Geode Pool configuration was [locators = %s, servers = %s]", + pool, pool.getLocators(), pool.getServers())); + } + + private Pool defaultIfNull(Pool pool, String... poolNames) { + + for (String poolName : poolNames) { + pool = (pool != null ? pool : PoolManager.find(poolName)); + } + + return pool; + } + }; + } + + ConnectionEndpoint newConnectionEndpoint(String host, int port) { + return new ConnectionEndpoint(host, port); + } + + @Bean + ClientCacheConfigurer registerClientMembershipListener() { + + return (beanName, bean) -> { + + ClientMembership.registerClientMembershipListener(new ClientMembershipListenerAdapter() { + + @Override + public void memberJoined(ClientMembershipEvent event) { + LATCH.countDown(); + } + }); + }; + } +} diff --git a/samples/boot/gemfire/src/main/java/sample/server/GemFireServer.java b/samples/boot/gemfire/src/main/java/sample/server/GemFireServer.java index 563a963..8708c7a 100644 --- a/samples/boot/gemfire/src/main/java/sample/server/GemFireServer.java +++ b/samples/boot/gemfire/src/main/java/sample/server/GemFireServer.java @@ -16,27 +16,15 @@ package sample.server; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Properties; -import java.util.concurrent.TimeUnit; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.GemFireCache; -import org.apache.geode.cache.Region; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Profile; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.data.gemfire.CacheFactoryBean; -import org.springframework.data.gemfire.ReplicatedRegionFactoryBean; -import org.springframework.data.gemfire.server.CacheServerFactoryBean; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; +import org.springframework.data.gemfire.config.annotation.CacheServerConfigurer; +import org.springframework.data.gemfire.config.annotation.EnableManager; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; /** @@ -51,103 +39,31 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl * @since 1.2.1 */ // tag::class[] -@SpringBootApplication -@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 20) // <1> +@SpringBootApplication // <1> +@CacheServerApplication(name = "SpringSessionDataGeodeServerBootSample", logLevel = "warning") // <2> +@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 20) // <3> +@EnableManager(start = true) // <4> @SuppressWarnings("unused") public class GemFireServer { - static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config"; - public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(GemFireServer.class); springApplication.setWebApplicationType(WebApplicationType.NONE); springApplication.run(args); } + // Required to resolve property placeholders in Spring @Value annotations. @Bean static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { // <2> - - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", applicationName()); - //gemfireProperties.setProperty("log-file", "gemfire-server.log"); - gemfireProperties.setProperty("log-level", logLevel()); - gemfireProperties.setProperty("jmx-manager", "true"); - gemfireProperties.setProperty("jmx-manager-start", "true"); - - return gemfireProperties; - } - - String applicationName() { - return "spring-session-data-gemfire-boot-sample:".concat(getClass().getSimpleName()); - } - - String logLevel() { - return System.getProperty("spring-session-data-gemfire.log.level", DEFAULT_GEMFIRE_LOG_LEVEL); - } - @Bean - CacheFactoryBean gemfireCache() { // <3> + CacheServerConfigurer cacheServerPortConfigurer( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <5> - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setProperties(gemfireProperties()); - - return gemfireCache; - } - - @Bean - CacheServerFactoryBean gemfireCacheServer(GemFireCache gemfireCache, - @Value("${spring-session-data-gemfire.cache.server.bind-address:localhost}") String bindAddress, - @Value("${spring-session-data-gemfire.cache.server.hostname-for-clients:localhost}") String hostnameForClients, - @Value("${spring-session-data-gemfire.cache.server.port:40404}") int port) { // <4> - - CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean(); - - gemfireCacheServer.setAutoStartup(true); - gemfireCacheServer.setBindAddress(bindAddress); - gemfireCacheServer.setCache((Cache) gemfireCache); - gemfireCacheServer.setHostNameForClients(hostnameForClients); - gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue()); - gemfireCacheServer.setPort(port); - - return gemfireCacheServer; - } - - @Bean(name = "Example") - @Profile("debug") - ReplicatedRegionFactoryBean exampleRegion(GemFireCache gemfireCache) { - - ReplicatedRegionFactoryBean exampleRegionFactory = new ReplicatedRegionFactoryBean<>(); - - exampleRegionFactory.setCache(gemfireCache); - exampleRegionFactory.setClose(false); - - return exampleRegionFactory; - } - - @Autowired - private GemFireCache gemfireCache; - - @Bean - @Profile("debug") - CommandLineRunner exampleInitializer() { - - return args -> { - - Region example = this.gemfireCache.getRegion("/Example"); - - String key = "time"; - - example.put(key, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:ss a"))); - - System.err.printf("Example.get(%s) is [%s]%n", key, example.get(key)); - }; + return (beanName, cacheServerFactoryBean) -> + cacheServerFactoryBean.setPort(port); } } // end::class[] diff --git a/samples/boot/gemfire/src/main/java/sample/server/NativeGemFireServer.java b/samples/boot/gemfire/src/main/java/sample/server/NativeGemFireServer.java index 80d11e3..4caa128 100644 --- a/samples/boot/gemfire/src/main/java/sample/server/NativeGemFireServer.java +++ b/samples/boot/gemfire/src/main/java/sample/server/NativeGemFireServer.java @@ -76,6 +76,7 @@ public final class NativeGemFireServer implements Runnable { } private static void writeStringTo(File file, String fileContents) { + PrintWriter fileWriter = null; try { @@ -92,6 +93,7 @@ public final class NativeGemFireServer implements Runnable { } } } + private NativeGemFireServer(String[] args) { this.args = nullSafeStringArray(args); } @@ -122,11 +124,13 @@ public final class NativeGemFireServer implements Runnable { } private String applicationName(String applicationName) { + return StringUtils.hasText(applicationName) ? applicationName : "spring-session-data-gemfire.boot.sample." + NativeGemFireServer.class.getSimpleName(); } private Properties gemfireProperties(String applicationName) { + Properties gemfireProperties = new Properties(); gemfireProperties.setProperty("name", applicationName); @@ -143,6 +147,7 @@ public final class NativeGemFireServer implements Runnable { } private Cache createRegion(Cache gemfireCache) { + RegionFactory regionFactory = gemfireCache.createRegionFactory(RegionShortcut.PARTITION); @@ -162,6 +167,7 @@ public final class NativeGemFireServer implements Runnable { } private Cache addCacheServer(Cache gemfireCache) throws IOException { + CacheServer cacheServer = gemfireCache.addCacheServer(); cacheServer.setBindAddress(GEMFIRE_CACHE_SERVER_HOST); @@ -173,6 +179,7 @@ public final class NativeGemFireServer implements Runnable { } private Cache registerShutdownHook(final Cache gemfireCache) { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { if (gemfireCache != null) { diff --git a/samples/javaconfig/gemfire-clientserver/spring-session-sample-javaconfig-gemfire-clientserver.gradle b/samples/javaconfig/gemfire-clientserver/spring-session-sample-javaconfig-gemfire-clientserver.gradle index 8579205..efe43e5 100644 --- a/samples/javaconfig/gemfire-clientserver/spring-session-sample-javaconfig-gemfire-clientserver.gradle +++ b/samples/javaconfig/gemfire-clientserver/spring-session-sample-javaconfig-gemfire-clientserver.gradle @@ -1,5 +1,6 @@ apply plugin: 'io.spring.convention.spring-sample-war' apply plugin: "gemfire-server" +apply plugin: "application" apply from: IDE_GRADLE @@ -25,3 +26,5 @@ dependencies { integrationTestRuntime "org.springframework.shell:spring-shell" } + +mainClassName = "sample.ServerConfig" diff --git a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ClientConfig.java b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ClientConfig.java index 2c73b8c..2c0668a 100644 --- a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ClientConfig.java +++ b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ClientConfig.java @@ -17,200 +17,33 @@ package sample; 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 java.util.concurrent.atomic.AtomicReference; -import org.apache.geode.cache.Region; -import org.apache.geode.cache.client.ClientCache; -import org.apache.geode.cache.client.Pool; -import org.apache.geode.cache.client.PoolManager; -import org.apache.geode.cache.client.internal.PoolImpl; -import org.apache.geode.management.membership.ClientMembership; -import org.apache.geode.management.membership.ClientMembershipEvent; -import org.apache.geode.management.membership.ClientMembershipListenerAdapter; - -import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; -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.config.xml.GemfireConstants; -import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; -import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; -import org.springframework.util.Assert; // tag::class[] -@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30, poolName = "DEFAULT") // <1> -public class ClientConfig { - - static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(60); - - static final CountDownLatch LATCH = new CountDownLatch(1); - - static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning"; - static final String GEMFIRE_DEFAULT_POOL_NAME = "DEFAULT"; - static final String PROXY_HOST = "dummy.example.com"; - static final String PROXY_PORT = "3128"; +@ClientCacheApplication(name = "SpringSessionDataGeodeClientJavaConfigSample", logLevel = "warning", + pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true) // <1> +@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30, poolName = "DEFAULT") // <2> +public class ClientConfig extends IntegrationTestConfig { + // Required to resolve property placeholders in Spring @Value annotations. @Bean static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { // <2> - Properties gemfireProperties = new Properties(); - gemfireProperties.setProperty("name", applicationName()); - // gemfireProperties.setProperty("log-file", "gemfire-client.log"); - gemfireProperties.setProperty("log-level", logLevel()); - return gemfireProperties; - } - - String applicationName() { - return "spring-session-data-gemfire-clientserver-javaconfig-sample:".concat(getClass().getSimpleName()); - } - - String logLevel() { - return System.getProperty("spring.session.data.gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL); - } - @Bean - ClientCacheFactoryBean gemfireCache( - @Value("${spring.session.data.gemfire.host:" + ServerConfig.SERVER_HOST + "}") String host, - @Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) { // <3> + ClientCacheConfigurer clientCacheServerPortConfigurer( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <3> - 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(intValue(TimeUnit.SECONDS.toMillis(15))); - clientCacheFactory.setRetryAttempts(1); - clientCacheFactory.setSubscriptionEnabled(true); - clientCacheFactory.setThreadLocalConnections(false); - clientCacheFactory.setServers(Collections.singletonList(newConnectionEndpoint(host, port))); - - registerClientMembershipListener(); // <5> - - return clientCacheFactory; + return (beanName, clientCacheFactoryBean) -> + clientCacheFactoryBean.setServers(Collections.singletonList( + newConnectionEndpoint("localhost", port))); } - - int intValue(Number number) { - return number.intValue(); - } - - ConnectionEndpoint newConnectionEndpoint(String host, int port) { - return new ConnectionEndpoint(host, port); - } - - void registerClientMembershipListener() { - ClientMembership.registerClientMembershipListener( - new ClientMembershipListenerAdapter() { - @Override - public void memberJoined(ClientMembershipEvent event) { - LATCH.countDown(); - } - }); - } - - @Bean - BeanPostProcessor gemfireClientServerReadyBeanPostProcessor( - @Value("${spring-session-data-gemfire.cache.server.host:localhost}") final String host, - @Value("${spring-session-data-gemfire.cache.server.port:12480}") final int port) { // <5> - - return new BeanPostProcessor() { - - private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true); - private final AtomicReference defaultPool = new AtomicReference(null); - - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (shouldCheckWhetherGemFireServerIsRunning(bean, beanName)) { - try { - validateCacheClientNotified(); - validateCacheClientSubscriptionQueueConnectionEstablished(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - return bean; - } - - private boolean shouldCheckWhetherGemFireServerIsRunning(Object bean, String beanName) { - return (isGemFireRegion(bean, beanName) - ? checkGemFireServerIsRunning.compareAndSet(true, false) - : whenGemFireCache(bean, beanName)); - } - - private boolean isGemFireRegion(Object bean, String beanName) { - return (GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME.equals(beanName) - || bean instanceof Region); - } - - private boolean whenGemFireCache(Object bean, String beanName) { - if (bean instanceof ClientCache) { - defaultPool.compareAndSet(null, ((ClientCache) bean).getDefaultPool()); - } - - return false; - } - - private void validateCacheClientNotified() throws InterruptedException { - boolean didNotTimeout = LATCH.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); - - Assert.state(didNotTimeout, String.format( - "GemFire Cache Server failed to start on host [%s] and port [%d]", host, port)); - } - - @SuppressWarnings("all") - private void validateCacheClientSubscriptionQueueConnectionEstablished() throws InterruptedException { - boolean cacheClientSubscriptionQueueConnectionEstablished = false; - - Pool pool = defaultIfNull(this.defaultPool.get(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, - GEMFIRE_DEFAULT_POOL_NAME); - - if (pool instanceof PoolImpl) { - long timeout = (System.currentTimeMillis() + DEFAULT_TIMEOUT); - - while (System.currentTimeMillis() < timeout - && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { - - synchronized (pool) { - TimeUnit.MILLISECONDS.timedWait(pool, 500L); - } - - } - - cacheClientSubscriptionQueueConnectionEstablished |= - ((PoolImpl) pool).isPrimaryUpdaterAlive(); - } - - Assert.state(cacheClientSubscriptionQueueConnectionEstablished, String.format( - "Cache client subscription queue connection not established; GemFire Pool was [%s];" - + " GemFire Pool configuration was [locators = %s, servers = %s]", - pool, pool.getLocators(), pool.getServers())); - } - - private Pool defaultIfNull(Pool pool, String... poolNames) { - for (String poolName : poolNames) { - pool = (pool != null ? pool : PoolManager.find(poolName)); - } - - return pool; - } - - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - }; - } - // end::class[] } +// end::class[] diff --git a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/IntegrationTestConfig.java b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/IntegrationTestConfig.java new file mode 100644 index 0000000..e8574fa --- /dev/null +++ b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/IntegrationTestConfig.java @@ -0,0 +1,168 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sample; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.Pool; +import org.apache.geode.cache.client.PoolManager; +import org.apache.geode.cache.client.internal.PoolImpl; +import org.apache.geode.management.membership.ClientMembership; +import org.apache.geode.management.membership.ClientMembershipEvent; +import org.apache.geode.management.membership.ClientMembershipListenerAdapter; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; +import org.springframework.data.gemfire.config.xml.GemfireConstants; +import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.util.Assert; + +/** + * The IntegrationTestConfig class... + * + * @author John Blum + * @since 1.0.0 + */ +public abstract class IntegrationTestConfig { + + static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(60); + + static final CountDownLatch LATCH = new CountDownLatch(1); + + static final String GEMFIRE_DEFAULT_POOL_NAME = "DEFAULT"; + + @Bean + BeanPostProcessor clientServerReadyBeanPostProcessor( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <5> + + return new BeanPostProcessor() { + + private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true); + private final AtomicReference defaultPool = new AtomicReference<>(null); + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + + if (shouldCheckWhetherGemFireServerIsRunning(bean, beanName)) { + try { + validateCacheClientNotified(); + validateCacheClientSubscriptionQueueConnectionEstablished(); + } + catch (InterruptedException cause) { + Thread.currentThread().interrupt(); + } + } + + return bean; + } + + private boolean shouldCheckWhetherGemFireServerIsRunning(Object bean, String beanName) { + + return (isGemFireRegion(bean, beanName) + ? checkGemFireServerIsRunning.compareAndSet(true, false) + : whenGemFireCache(bean, beanName)); + } + + private boolean isGemFireRegion(Object bean, String beanName) { + + return (GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME.equals(beanName) + || bean instanceof Region); + } + + private boolean whenGemFireCache(Object bean, String beanName) { + + if (bean instanceof ClientCache) { + defaultPool.compareAndSet(null, ((ClientCache) bean).getDefaultPool()); + } + + return false; + } + + private void validateCacheClientNotified() throws InterruptedException { + + boolean didNotTimeout = LATCH.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + + Assert.state(didNotTimeout, String.format( + "Apache Geode Cache Server failed to start on host [%s] and port [%d]", "localhost", port)); + } + + @SuppressWarnings("all") + private void validateCacheClientSubscriptionQueueConnectionEstablished() throws InterruptedException { + + boolean cacheClientSubscriptionQueueConnectionEstablished = false; + + Pool pool = defaultIfNull(this.defaultPool.get(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, + GEMFIRE_DEFAULT_POOL_NAME); + + if (pool instanceof PoolImpl) { + + long timeout = (System.currentTimeMillis() + DEFAULT_TIMEOUT); + + while (System.currentTimeMillis() < timeout && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { + synchronized (pool) { + TimeUnit.MILLISECONDS.timedWait(pool, 500L); + } + + } + + cacheClientSubscriptionQueueConnectionEstablished |= ((PoolImpl) pool).isPrimaryUpdaterAlive(); + } + + Assert.state(cacheClientSubscriptionQueueConnectionEstablished, + String.format("Cache client subscription queue connection not established; Geode Pool was [%s];" + + " Geode Pool configuration was [locators = %s, servers = %s]", + pool, pool.getLocators(), pool.getServers())); + } + + private Pool defaultIfNull(Pool pool, String... poolNames) { + + for (String poolName : poolNames) { + pool = (pool != null ? pool : PoolManager.find(poolName)); + } + + return pool; + } + }; + } + + ConnectionEndpoint newConnectionEndpoint(String host, int port) { + return new ConnectionEndpoint(host, port); + } + + @Bean + ClientCacheConfigurer registerClientMembershipListener() { + + return (beanName, bean) -> { + + ClientMembership.registerClientMembershipListener(new ClientMembershipListenerAdapter() { + + @Override + public void memberJoined(ClientMembershipEvent event) { + LATCH.countDown(); + } + }); + }; + } +} diff --git a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ServerConfig.java b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ServerConfig.java index 255cda4..69f04ab 100644 --- a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ServerConfig.java +++ b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/ServerConfig.java @@ -17,86 +17,38 @@ package sample; import java.io.IOException; -import java.util.Properties; -import java.util.concurrent.TimeUnit; - -import org.apache.geode.cache.Cache; -import org.apache.geode.cache.GemFireCache; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.data.gemfire.CacheFactoryBean; -import org.springframework.data.gemfire.server.CacheServerFactoryBean; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; +import org.springframework.data.gemfire.config.annotation.CacheServerConfigurer; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; // tag::class[] -@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <1> +@CacheServerApplication(name = "SpringSessionSampleJavaConfigGemFireClientServer", logLevel = "warning") // <1> +@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <2> public class ServerConfig { - 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> + public static void main(String[] args) throws IOException { new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook(); } + // Required to resolve property placeholders in Spring @Value annotations. @Bean static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } - Properties gemfireProperties() { // <2> - - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", applicationName()); - gemfireProperties.setProperty("mcast-port", "0"); - // gemfireProperties.setProperty("log-file", "gemfire-server.log"); - gemfireProperties.setProperty("log-level", logLevel()); - // gemfireProperties.setProperty("jmx-manager", "true"); - // gemfireProperties.setProperty("jmx-manager-start", "true"); - - 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); - } - @Bean - CacheFactoryBean gemfireCache() { // <3> + CacheServerConfigurer cacheServerPortConfigurer( + @Value("${spring.session.data.geode.cache.server.port:40404}") int port) { // <3> - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setProperties(gemfireProperties()); - - return gemfireCache; - } - - @Bean - CacheServerFactoryBean gemfireCacheServer(GemFireCache gemfireCache, - @Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) { // <4> - - CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean(); - - gemfireCacheServer.setAutoStartup(true); - gemfireCacheServer.setBindAddress(SERVER_HOST); - gemfireCacheServer.setCache((Cache) gemfireCache); - gemfireCacheServer.setHostNameForClients(SERVER_HOST); - gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue()); - gemfireCacheServer.setPort(port); - - return gemfireCacheServer; + return (beanName, cacheServerFactoryBean) -> { + cacheServerFactoryBean.setPort(port); + }; } } // end::class[] diff --git a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/SessionServlet.java b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/SessionServlet.java index 98f3ae8..0c1dc6c 100644 --- a/samples/javaconfig/gemfire-clientserver/src/main/java/sample/SessionServlet.java +++ b/samples/javaconfig/gemfire-clientserver/src/main/java/sample/SessionServlet.java @@ -28,15 +28,17 @@ import javax.servlet.http.HttpServletResponse; @WebServlet("/session") public class SessionServlet extends HttpServlet { + private static final long serialVersionUID = 2878267318695777395L; + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String attributeName = request.getParameter("attributeName"); String attributeValue = request.getParameter("attributeValue"); + request.getSession().setAttribute(attributeName, attributeValue); response.sendRedirect(request.getContextPath() + "/"); } - - private static final long serialVersionUID = 2878267318695777395L; } // tag::end[] diff --git a/samples/javaconfig/gemfire-p2p/src/main/java/sample/Config.java b/samples/javaconfig/gemfire-p2p/src/main/java/sample/Config.java index 7be4704..ae83e7a 100644 --- a/samples/javaconfig/gemfire-p2p/src/main/java/sample/Config.java +++ b/samples/javaconfig/gemfire-p2p/src/main/java/sample/Config.java @@ -16,38 +16,15 @@ package sample; -import java.util.Properties; - -import org.springframework.context.annotation.Bean; -import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.config.annotation.EnableManager; +import org.springframework.data.gemfire.config.annotation.PeerCacheApplication; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; // tag::class[] -@EnableGemFireHttpSession // <1> +@PeerCacheApplication(name = "SpringSessionSampleJavaConfigGemFireP2p", logLevel = "warning") // <1> +@EnableGemFireHttpSession // <2> +@EnableManager(start = true) // <3> public class Config { - @Bean - Properties gemfireProperties() { // <2> - Properties gemfireProperties = new Properties(); - - gemfireProperties.setProperty("name", "GemFireP2PHttpSessionSample"); - gemfireProperties.setProperty("mcast-port", "0"); - gemfireProperties.setProperty("log-level", - System.getProperty("sample.httpsession.gemfire.log-level", "warning")); - gemfireProperties.setProperty("jmx-manager", "true"); - gemfireProperties.setProperty("jmx-manager-start", "true"); - - return gemfireProperties; - } - - @Bean - CacheFactoryBean gemfireCache() { // <3> - CacheFactoryBean gemfireCache = new CacheFactoryBean(); - - gemfireCache.setClose(true); - gemfireCache.setProperties(gemfireProperties()); - - return gemfireCache; - } } // end::class[] diff --git a/samples/javaconfig/gemfire-p2p/src/main/java/sample/SessionServlet.java b/samples/javaconfig/gemfire-p2p/src/main/java/sample/SessionServlet.java index 98f3ae8..0c1dc6c 100644 --- a/samples/javaconfig/gemfire-p2p/src/main/java/sample/SessionServlet.java +++ b/samples/javaconfig/gemfire-p2p/src/main/java/sample/SessionServlet.java @@ -28,15 +28,17 @@ import javax.servlet.http.HttpServletResponse; @WebServlet("/session") public class SessionServlet extends HttpServlet { + private static final long serialVersionUID = 2878267318695777395L; + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String attributeName = request.getParameter("attributeName"); String attributeValue = request.getParameter("attributeValue"); + request.getSession().setAttribute(attributeName, attributeValue); response.sendRedirect(request.getContextPath() + "/"); } - - private static final long serialVersionUID = 2878267318695777395L; } // tag::end[] diff --git a/samples/xml/gemfire-clientserver/spring-session-sample-xml-gemfire-clientserver.gradle b/samples/xml/gemfire-clientserver/spring-session-sample-xml-gemfire-clientserver.gradle index 8579205..efe43e5 100644 --- a/samples/xml/gemfire-clientserver/spring-session-sample-xml-gemfire-clientserver.gradle +++ b/samples/xml/gemfire-clientserver/spring-session-sample-xml-gemfire-clientserver.gradle @@ -1,5 +1,6 @@ apply plugin: 'io.spring.convention.spring-sample-war' apply plugin: "gemfire-server" +apply plugin: "application" apply from: IDE_GRADLE @@ -25,3 +26,5 @@ dependencies { integrationTestRuntime "org.springframework.shell:spring-shell" } + +mainClassName = "sample.ServerConfig" diff --git a/samples/xml/gemfire-clientserver/src/main/java/sample/GemFireClientServerReadyBeanPostProcessor.java b/samples/xml/gemfire-clientserver/src/main/java/sample/ClientServerReadyBeanPostProcessor.java similarity index 87% rename from samples/xml/gemfire-clientserver/src/main/java/sample/GemFireClientServerReadyBeanPostProcessor.java rename to samples/xml/gemfire-clientserver/src/main/java/sample/ClientServerReadyBeanPostProcessor.java index ab51f0b..f68325d 100644 --- a/samples/xml/gemfire-clientserver/src/main/java/sample/GemFireClientServerReadyBeanPostProcessor.java +++ b/samples/xml/gemfire-clientserver/src/main/java/sample/ClientServerReadyBeanPostProcessor.java @@ -36,7 +36,7 @@ import org.springframework.data.gemfire.config.xml.GemfireConstants; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.util.Assert; -public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProcessor { +public class ClientServerReadyBeanPostProcessor implements BeanPostProcessor { private static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(60); @@ -55,16 +55,17 @@ public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProces ); } - @Value("${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}") + @Value("${spring.session.data.geode.cache.server.port:${application.geode.client-server.port:40404}}") private int port; - @Value("${application.gemfire.client-server.host:localhost}") + @Value("${application.geode.client-server.host:localhost}") private String host; private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true); private final AtomicReference gemfirePool = new AtomicReference(null); public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (shouldCheckWhetherGemFireServerIsRunning(bean, beanName)) { try { validateCacheClientNotified(); @@ -79,17 +80,20 @@ public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProces } private boolean shouldCheckWhetherGemFireServerIsRunning(Object bean, String beanName) { + return (isGemFireRegion(bean, beanName) ? this.checkGemFireServerIsRunning.compareAndSet(true, false) : whenGemFirePool(bean, beanName)); } private boolean isGemFireRegion(Object bean, String beanName) { + return (GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME.equals(beanName) || bean instanceof Region); } private boolean whenGemFirePool(Object bean, String beanName) { + if (bean instanceof Pool) { this.gemfirePool.compareAndSet(null, (Pool) bean); } @@ -98,24 +102,26 @@ public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProces } private void validateCacheClientNotified() throws InterruptedException { + boolean didNotTimeout = LATCH.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); Assert.state(didNotTimeout, String.format( - "GemFire Cache Server failed to start on host [%s] and port [%d]", this.host, this.port)); + "Apache Geode Cache Server failed to start on host [%s] and port [%d]", this.host, this.port)); } @SuppressWarnings("all") private void validateCacheClientSubscriptionQueueConnectionEstablished() throws InterruptedException { + boolean cacheClientSubscriptionQueueConnectionEstablished = false; Pool pool = defaultIfNull(this.gemfirePool.get(), GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, GEMFIRE_DEFAULT_POOL_NAME); if (pool instanceof PoolImpl) { + long timeout = (System.currentTimeMillis() + DEFAULT_TIMEOUT); - while (System.currentTimeMillis() < timeout - && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { + while (System.currentTimeMillis() < timeout && !((PoolImpl) pool).isPrimaryUpdaterAlive()) { synchronized (pool) { TimeUnit.MILLISECONDS.timedWait(pool, 500L); @@ -127,13 +133,14 @@ public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProces ((PoolImpl) pool).isPrimaryUpdaterAlive(); } - Assert.state(cacheClientSubscriptionQueueConnectionEstablished, String.format( - "Cache client subscription queue connection not established; GemFire Pool was [%s];" - + " GemFire Pool configuration was [locators = %s, servers = %s]", + Assert.state(cacheClientSubscriptionQueueConnectionEstablished, + String.format("Cache client subscription queue connection not established; Geode Pool was [%s];" + + " Geode Pool configuration was [locators = %s, servers = %s]", pool, pool.getLocators(), pool.getServers())); } private Pool defaultIfNull(Pool pool, String... poolNames) { + for (String poolName : poolNames) { pool = (pool != null ? pool : PoolManager.find(poolName)); } @@ -145,4 +152,3 @@ public class GemFireClientServerReadyBeanPostProcessor implements BeanPostProces return bean; } } -// tag::end[] diff --git a/samples/xml/gemfire-clientserver/src/main/java/sample/ServerConfig.java b/samples/xml/gemfire-clientserver/src/main/java/sample/ServerConfig.java index 3f9af2d..f7da77f 100644 --- a/samples/xml/gemfire-clientserver/src/main/java/sample/ServerConfig.java +++ b/samples/xml/gemfire-clientserver/src/main/java/sample/ServerConfig.java @@ -20,7 +20,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; -@SuppressWarnings("resource") // tag::class[] @Configuration // <1> @ImportResource("META-INF/spring/session-server.xml") // <2> diff --git a/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/application.properties b/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/application.properties index 31648e7..1df7b96 100644 --- a/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/application.properties +++ b/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/application.properties @@ -1,2 +1,2 @@ -application.gemfire.client-server.host=localhost -application.gemfire.client-server.port=40404 +application.geode.client-server.host=localhost +application.geode.client-server.port=40404 diff --git a/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/session-server.xml b/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/session-server.xml index cf23bcc..43d959b 100644 --- a/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/session-server.xml +++ b/samples/xml/gemfire-clientserver/src/main/resources/META-INF/spring/session-server.xml @@ -13,17 +13,13 @@ "> - - - + - GemFireClientServerHttpSessionXmlSample - 0 - + SpringSessionSampleXmlGemFireClientServer ${spring.session.data.gemfire.log-level:warning} - + - + + bind-address="${application.geode.client-server.host:localhost}" + host-name-for-clients="${application.geode.client-server.host:localhost}" + port="${spring.session.data.geode.cache.server.port:${application.geode.client-server.port:40404}}"/> - + diff --git a/samples/xml/gemfire-clientserver/src/main/webapp/WEB-INF/spring/session-client.xml b/samples/xml/gemfire-clientserver/src/main/webapp/WEB-INF/spring/session-client.xml index bde07b3..7dff9c1 100644 --- a/samples/xml/gemfire-clientserver/src/main/webapp/WEB-INF/spring/session-client.xml +++ b/samples/xml/gemfire-clientserver/src/main/webapp/WEB-INF/spring/session-client.xml @@ -13,36 +13,27 @@ "> - - - - + - + - - ${spring.session.data.gemfire.log-level:warning} + ${spring.session.data.geode.log-level:warning} - + - - - + + + - + diff --git a/samples/xml/gemfire-p2p/src/main/java/sample/SessionServlet.java b/samples/xml/gemfire-p2p/src/main/java/sample/SessionServlet.java index f37983b..80b162a 100644 --- a/samples/xml/gemfire-p2p/src/main/java/sample/SessionServlet.java +++ b/samples/xml/gemfire-p2p/src/main/java/sample/SessionServlet.java @@ -26,15 +26,17 @@ import javax.servlet.http.HttpServletResponse; // tag::class[] public class SessionServlet extends HttpServlet { + private static final long serialVersionUID = 2878267318695777395L; + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String attributeName = request.getParameter("attributeName"); String attributeValue = request.getParameter("attributeValue"); + request.getSession().setAttribute(attributeName, attributeValue); response.sendRedirect(request.getContextPath() + "/"); } - - private static final long serialVersionUID = 2878267318695777395L; } // end::class[] diff --git a/samples/xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml b/samples/xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml index 2e408ab..be4be0b 100644 --- a/samples/xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml +++ b/samples/xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml @@ -12,23 +12,23 @@ "> - + - - - + - GemFireP2PHttpSessionXmlSample - 0 - ${sample.httpsession.gemfire.log-level:warning} + SpringSessionSampleXmlGemFireP2p + ${spring.session.data.geode.log-level:warning} true true - + + + +