= Spring Session - HttpSession with GemFire Client/Server (Quick Start)
John Blum
:toc:

This guide describes how to configure Spring Session to transparently leverage Pivotal GemFire to back a web application's
`HttpSession` using Java Configuration.

NOTE: The completed guide can be found in the <<httpsession-gemfire-clientserver-java-sample-app,HttpSession with GemFire (Client/Server) Sample Application>>.

== Updating Dependencies
Before using Spring Session, you must ensure that the required dependencies are included.
If you are using Maven, include the following `dependencies` in your _pom.xml_:

.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
	<!-- ... -->

	<dependency>
		<groupId>org.springframework.session</groupId>
		<artifactId>spring-session-data-gemfire</artifactId>
		<version>{spring-session-version}</version>
		<type>pom</type>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-web</artifactId>
		<version>{spring-version}</version>
	</dependency>
</dependencies>
----

ifeval::["{version-snapshot}" == "true"]
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
If you are using Maven, include the following `repository` declaration in your _pom.xml_:

.pom.xml
[source,xml]
----
<repositories>
	<!-- ... -->

	<repository>
		<id>spring-snapshot</id>
		<url>https://repo.spring.io/libs-snapshot</url>
	</repository>
</repositories>
----
endif::[]

ifeval::["{version-milestone}" == "true"]
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
If you are using Maven, include the following `repository` declaration in your _pom.xml_:

.pom.xml
[source,xml]
----
<repositories>
	<!-- ... -->

	<repository>
		<id>spring-milestone</id>
		<url>https://repo.spring.io/libs-milestone</url>
	</repository>
</repositories>
----
endif::[]

// tag::config[]
[[httpsession-spring-java-configuration]]
== Spring Java Configuration

After adding the required dependencies and repository declarations, we can create our Spring configuration.
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession`
with an implementation backed by Spring Session and GemFire.

Add the following Spring Configuration:

[source,java]
----
include::{samples-dir}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://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/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.

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.

The `BeanPostProcessor` uses a GemFire http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html[ClientMembershipListener]
that will be notified when the client has successfully connected to the server. Once a connection has been established,
the listener releases the latch that the `BeanPostProcessor` will wait on (up to the specified timeout) in the
`postProcessAfterInitialization` callback to block the client.

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:

* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*").
* `clientRegionShort` - specifies GemFire's http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
with a GemFire http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
(default is `PROXY`).  This attribute is only used when configuring client Region.
* `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/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
(default is `PARTITION`).  This attribute is only used when configuring server Regions, or when a p2p topology is employed.

NOTE: It is important to note that the GemFire client Region name must match a server Region by the same name if
the client Region is a `PROXY` or `CACHING_PROXY`.  Names are not required to match if the client Region used to
store Spring Sessions is `LOCAL`, however, keep in mind that your session state will not be propagated to the server
and you lose all benefits of using GemFire to store and manage distributed, replicated session state information
in a cluster.

NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when
a peer-to-peer (P2P) topology, and more specifically, a GemFire peer cache is used.

=== Server Configuration

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.

In this sample, we will use the following GemFire Server Java Configuration:

[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.

== Java Servlet Container Initialization

Our <<httpsession-spring-java-configuration,Spring Java Configuration>> created a Spring bean named `springSessionRepositoryFilter`
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession`
with a custom implementation backed by Spring Session and GemFire.

In order for our `Filter` to do its magic, Spring needs to load our `ClientConfig` class. We also need to ensure our
Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. Fortunately, Spring Session
provides a utility class named `AbstractHttpSessionApplicationInitializer` to make both of these steps extremely easy.

You can find an example below:

.src/main/java/sample/Initializer.java
[source,java]
----
include::{samples-dir}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`.

<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
This ensures that a Spring bean named `springSessionRepositoryFilter` is registered with our Servlet Container
and used for every request.
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily allow Spring to load our `ClientConfig`.
// end::config[]

[[httpsession-gemfire-clientserver-java-sample-app]]
== HttpSession with GemFire (Client/Server) Sample Application


=== Running the httpsession-gemfire-clientserver Sample Application

You can run the sample by obtaining the {download-url}[source code] and invoking the following commands.

First, you need to run the server using:

----
$ ./gradlew :samples:httpsession-gemfire-clientserver:run [-Dsample.httpsession.gemfire.log-level=info]
----

Then, in a separate terminal, you run the client using:

----
$ ./gradlew :samples:httpsession-gemfire-clientserver:tomcatRun [-Dsample.httpsession.gemfire.log-level=info]
----

You should now be able to access the application at http://localhost:8080/.  In this sample, the web application
is the client cache and the server is standalone.

=== Exploring the httpsession-gemfire-clientserver Sample Application

Try using the application. Fill out the form with the following information:

* **Attribute Name:** _username_
* **Attribute Value:** _john_

Now click the **Set Attribute** button. You should now see the values displayed in the table.

=== How does it work?

We interact with the standard `HttpSession` in the `SessionServlet` shown below:

.src/main/java/sample/SessionServlet.java
[source,java]
----
include::{samples-dir}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.
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).

NOTE: The following instructions assume you have a local GemFire installation.  For more information on installation,
see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].

If you like, you can easily remove the session using `gfsh`. For example, on a Linux-based system type the following
at the command-line:

	$ gfsh

Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):

....
gfsh>connect --jmx-manager=localhost[1099]

gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'

Result     : true
startCount : 0
endCount   : 20
Rows       : 1

Result
------------------------------------
70002719-3c54-4c20-82c3-e7faa6b718f3

NEXT_STEP_NAME : END

gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
....

NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].

Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed.

Alternatively, you can wait **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.
