Table of Contents
Spring Session provides an API and implementations for managing a user’s session information. It also provides transparent integration with:
javax.servlet.http.HttpSession
in an application container (e.g. Tomcat) neutral way.
Additional features include:
HttpSession
alive when receiving messages
via a WebSocket
.
If you are looking to get started with Spring Session right of way, the best place to start is with our Sample Applications.
Table 2.1. Sample Application using Spring Boot
Source | Description | Guide |
---|---|---|
Demonstrates how to use Spring Session to manage the | ||
HttpSession with Spring Boot and Apache Geode using Scoped Proxies | Demonstrates how to use Spring Session to manage the | HttpSession with Spring Boot and Apache Geode using Scoped Proxies Guide |
Table 2.2. Sample Applications using Spring’s Java-based configuration
Source | Description | Guide |
---|---|---|
Demonstrates how to use Spring Session to manage the | ||
Demonstrates how to use Spring Session to manage the |
Table 2.3. Sample Applications using Spring’s XML-based configuration
Source | Description | Guide |
---|---|---|
Demonstrates how to use Spring Session to manage the | ||
Demonstrates how to use Spring Session to manage the |
Spring Session provides transparent integration with javax.servlet.http.HttpSession
. This means that developers
can replace the HttpSession
implementation with an implementation that is backed by Spring Session.
We already mentioned that Spring Session provides transparent integration with HttpSession
, but what benefits
do we get out of this?
HttpSession
alive when receiving messages
via a WebSocket
.
When 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 for managing Spring Sessions using Apache Geode include:
Additionally, Apache Geode supports site-to-site replication using WAN technology. The ability to configure and use Apache Geode’s WAN functionality is independent of Spring Session, and beyond the scope of this document.
More details on configuring Apache Geode WAN functionality using Spring Data Geode can be found here.
The 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 Apache Geode server has significantly different and unique JVM heap requirements than 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 1 or more connections to a remote cluster
of Apache Geode servers that will manage access to all HttpSession
state.
You can configure a Client-Server topology with either:
This section describes how to configure Apache Geode’s Client-Server topology to manage an HttpSession
with Java-based configuration.
![]() | Note |
---|---|
The HttpSession with Apache Geode (Client-Server) provides a working sample demonstrating how to
integrate Spring Session with Apache Geode to manage the |
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:
@ClientCacheApplication(name = "SpringSessionDataGeodeClientJavaConfigSample", logLevel = "warning", pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true)@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30, poolName = "DEFAULT")
public class ClientConfig extends IntegrationTestConfig { // Required to resolve property placeholders in Spring @Value annotations. @Bean static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Bean ClientCacheConfigurer clientCacheServerPortConfigurer( @Value("${spring.session.data.geode.cache.server.port:40404}") int port) {
return (beanName, clientCacheFactoryBean) -> clientCacheFactoryBean.setServers(Collections.singletonList( newConnectionEndpoint("localhost", port))); } }
First, we declare our Web application to be a Apache Geode cache client by annotating our | |
| |
Then, we adjust the port used by the cache client |
![]() | Tip |
---|---|
In typical Apache Geode production deployments, where the cluster includes potentially hundreds or thousands of Apache Geode servers (data nodes), it is more common for clients to connect to 1 or more Apache 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 Client/Server Deployment in the Apache Geode User Guide. |
![]() | Note |
---|---|
For more information on configuring _Spring Data Geode, refer to the 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:
clientRegionShortcut
- specifies Apache Geode data management policy
on the client with a Apache Geode 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 data management policy
on the server using a Apache Geode 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 Apache Geode client |
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 Java configuration to spin up an Apache Geode Server:
@CacheServerApplication(name = "SpringSessionSampleJavaConfigGemFireClientServer", logLevel = "warning")@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30)
public class ServerConfig { @SuppressWarnings("resource") 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(); } @Bean CacheServerConfigurer cacheServerPortConfigurer( @Value("${spring.session.data.geode.cache.server.port:40404}") int port) {
return (beanName, cacheServerFactoryBean) -> { cacheServerFactoryBean.setPort(port); }; } }
First, we use the new Spring Data Geode configuration annotation | |
(Optional) Then, the | |
Finally, we adjust the port that the |
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.
Our Spring Java Configuration 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 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:
src/main/java/sample/Initializer.java.
public class Initializer extends AbstractHttpSessionApplicationInitializer {public Initializer() { super(ClientConfig.class);
} }
![]() | Note |
---|---|
The name of our class ( |
The first step is to extend | |
|
This section describes how to configure Apache Geode’s Client-Server topology to manage an HttpSession
with XML-based configuration.
![]() | Note |
---|---|
The HttpSession with Apache Geode (Client-Server) using XML provides a working sample demonstrating
how to integrate Spring Session with Apache Geode to manage the |
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:
<context:annotation-config/> <context:property-placeholder location="classpath:META-INF/spring/application.properties"/> <bean class="sample.ClientServerReadyBeanPostProcessor"/><util:properties id="gemfireProperties"> <prop key="log-level">${spring.session.data.geode.log-level:warning}</prop> </util:properties>
<gfe:client-cache properties-ref="gemfireProperties" pool-name="gemfirePool"/>
<gfe:pool ping-interval="5000" read-timeout="15000" retry-attempts="1" subscription-enabled="true"> <gfe:server host="${application.geode.client-server.host}" port="${spring.session.data.geode.cache.server.port:${application.geode.client-server.port:40404}}"/> </gfe:pool>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration" p:maxInactiveIntervalInSeconds="30" p:poolName="DEFAULT"/>
(Optional) First, we can include a | |
We must create an instance of an Apache Geode | |
Then we configure a | |
Finally, a |
![]() | Tip |
---|---|
In typical Apache Geode production deployments, where the cluster includes potentially hundreds or thousands of Apache Geode servers (data nodes), it is more common for clients to connect to 1 or more Apache 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 Client/Server Deployment in the Apache Geode User Guide. |
![]() | Note |
---|---|
For more information on configuring _Spring Data Geode, refer to the Reference Guide. |
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 XML configuration to spin up an Apache Geode Server:
<context:annotation-config/> <context:property-placeholder location="classpath:META-INF/spring/application.properties"/><util:properties id="gemfireProperties"> <prop key="name">SpringSessionSampleXmlGemFireClientServer</prop> <prop key="log-level">${spring.session.data.gemfire.log-level:warning}</prop> <!-- <prop key="jmx-manager">true</prop> <prop key="jmx-manager-start">true</prop> --> </util:properties>
<gfe:cache properties-ref="gemfireProperties"/>
<gfe:cache-server auto-startup="true" 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}}"/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration" p:maxInactiveIntervalInSeconds="30"/>
(Optional) First, we can include a | |
We must configure an Apache Geode peer | |
Next, we define a | |
Finally, we enable the same Spring Session functionality we declared in the client XML configuration
by registering an instance of |
The Apache Geode Server gets bootstrapped with the following:
@Configuration@ImportResource("META-INF/spring/session-server.xml")
public class ServerConfig { public static void main(String[] args) { new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook(); } }
![]() | Tip |
---|---|
Rather than defining a simple Java class with a |
Our Spring XML Configuration 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.
We do this with the following configuration:
src/main/webapp/WEB-INF/web.xml.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/session-client.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
The ContextLoaderListener
reads the contextConfigLocation
context parameter value and picks up our session-client.xml configuration file.
Finally, we need to ensure that our Servlet container (i.e. Tomcat) uses our springSessionRepositoryFilter
for every request.
The following snippet performs this last step for us:
src/main/webapp/WEB-INF/web.xml.
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
The DelegatingFilterProxy
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
.
Perhaps a less common approach is to configure the Spring Session application as a peer member in the Apache Geode cluster using the Peer-To-Peer (P2P) topology. In this configuration, the Spring Session application would be an actual server (or data node) in the Apache 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. its data). However, there are other effective means of accomplishing similar data dependent computations, such as using Apache Geode’s Function Execution. Any of Apache Geode’s other features can be used when Apache Geode is serving as a provider in Spring Session.
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:
This section describes how to configure Apache Geode’s Peer-To-Peer (P2P) topology to manage an HttpSession
using Java-based configuration.
![]() | Note |
---|---|
The HttpSession with Apache Geode (P2P) provides a working sample demonstrating how to
integrate Spring Session with Apache Geode to manage the |
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:
@PeerCacheApplication(name = "SpringSessionSampleJavaConfigGemFireP2p", logLevel = "warning")@EnableGemFireHttpSession
@EnableManager(start = true)
public class Config { }
First, we use the new Spring Data Geode configuration annotation | |
Then, the | |
(Optionally) Finally, we annotated the |
![]() | Note |
---|---|
For more information on configuring _Spring Data Geode, refer to the 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:
clientRegionShortcut
- specifies Apache Geode data management policy
on the client with a Apache Geode 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 data management policy
on the server using a Apache Geode RegionShortcut
(default is PARTITION
). This attribute is only used when configuring server Regions
, or when a P2P topology is employed.
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
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:
src/main/java/sample/Initializer.java.
public class Initializer extends AbstractHttpSessionApplicationInitializer {public Initializer() { super(Config.class);
} }
![]() | Note |
---|---|
The name of our class ( |
The first step is to extend | |
|
This section describes how to configure Apache Geode’s Peer-To-Peer (P2P) topology to manage an HttpSession
using XML-based configuration.
![]() | Note |
---|---|
The HttpSession with Apache Geode (P2P) using XML provides a working sample demonstrating how to
integrate Spring Session with Apache Geode to manage the |
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:
src/main/webapp/WEB-INF/spring/session.xml.
<context:annotation-config/> <context:property-placeholder/><util:properties id="gemfireProperties"> <prop key="name">SpringSessionSampleXmlGemFireP2p</prop> <prop key="log-level">${spring.session.data.geode.log-level:warning}</prop> <prop key="jmx-manager">true</prop> <prop key="jmx-manager-start">true</prop> </util:properties>
<gfe:cache properties-ref="gemfireProperties" use-bean-factory-locator="false"/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"/>
(Optional) First, we can include a | |
We must configure an Apache Geode peer | |
Finally, we enable Spring Session functionality by registering an instance of |
![]() | Tip |
---|---|
Additionally, we have configured this data node (server) as a Apache Geode Manager as well using Apache 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 Geode, refer to the Reference Guide. |
The Spring XML Configuration 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.
We do this with the following configuration:
src/main/webapp/WEB-INF/web.xml.
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/*.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
The 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 HTTP request.
The following snippet performs this last step for us:
src/main/webapp/WEB-INF/web.xml.
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
The DelegatingFilterProxy
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
.
In order to transfer data between clients and servers, or when data is distributed/replicated between peer nodes in a cluster, the data must be serialized. In this case, the data in question is the Session’s state.
Anytime a Session is persisted or accessed in a client/server topology, the Session’s state is sent over-the-wire. Typically, a Spring Boot application with Spring Session enabled will be a client to the server(s) that form a cluster of nodes in Apache Geode or Pivotal GemFire.
On the server-side, Session state maybe distributed across several servers (data nodes) in the cluster to replicate the data in order to support high availability of the Session state. Using Pivotal GemFire or Apache Geode, data can be partitioned, or sharded, and a redundancy-level can be specified. When the data is distributed for replication, it must also be serialized to transfer the Session state among the peer nodes in the cluster.
Out-of-the-box, both Pivotal GemFire and Apache Geode support Java Serialization. There are many advantages to Java Serialization such as handling cycles in the object graph, or being universally supported by any application written in Java. However, Java Serialization is very verbose and not the most efficient over-the-wire format.
As such, Pivotal GemFire and Apache Geode provides its own serialization frameworks to serialize Java types:
As mentioned above, Apache Geode and Pivotal GemFire provide 2 additional serialization frameworks: Data Serialization and PDX Serialization.
Data Serialization is a very efficient format (i.e. fast and compact), with little overhead when compared to Java Serialization. It supports Delta Propagation by sending only the bits of data that actually changed as opposed to sending the entire object, which certainly cuts down on the amount of data sent over the network in addition to reducing the amount of IO when data is persisted or overflowed to disk.
However, Data Serialization incurs a CPU penalty anytime data is transferred over-the-wire, or persisted/overflowed to
and accessed from disk, since the receiving end performs a deserialization. In fact, anytime Delta Propagation
is used, the object must be deserialized on the receiving end in order to apply the "delta", since GemFire/Apache Geode
applies deltas by invoking a method on the object that implements the org.apache.geode.Delta
interface. Clearly,
you cannot invoke a method on a serialized object.
PDX, on the other hand, which stands for Portable Data Exchange, retains the form in which the data was sent.
For example, if a client sends data to a server in PDX format, the server will retain the data as PDX serialized bytes
and store them in the cache Region
for which the data access operation was targeted.
Additionally, PDX, as the name implies, is "portable", meaning it enables both Java and Native Language Clients, such as C, C++ and C# clients, to inter-operate on the same data set.
PDX even allows OQL queries to be performed on the serialized bytes without causing the objects to be deserialized in order to evaluate the query predicate and execute the query. This can be accomplished since Pivotal GemFire and Apache Geode maintains a "Type Registry" containing type meta-data for the objects that get serialized and stored in Apache Geode.
However, portability does come with a cost, having slightly more overhead than Data Serialization. Still, PDX is far more efficient and flexible than Java Serialization where type meta-data is actually stored in the serialized bytes of the object rather than in a separate Type Registry as in Pivotal GemFire and Apache Geode’s case.
PDX does not support Deltas. Technically, a PDX serializable object can be used in Delta Propagation by
implementing the org.apache.geode.Delta
interface, and only the "delta" will be sent, even in the context of PDX. But then, the PDX serialized object must be
deserialized to apply the delta. Remember, a method is invoked to apply the delta, which defeats the purpose of using
PDX in the first place.
When developing Native Clients (e.g. C++) that manage data in a Pivotal GemFire or Apache Geode cluster, or even when mixing Native Clients with Java clients, typically there will not be any associated Java types provided on the classpath of the servers in the cluster. With PDX, it is not necessary to provide the Java types on the classpath, and many customers who only develop and use Native Clients will not provide any Java types for the corresponding C# types.
GemFire and Apache Geode also support JSON serialized to/from PDX. In this case, it is very likely that Java types will not be provided on the servers classpath since many different languages (e.g. JavaScript, Python, Ruby) supporting JSON can be used with Pivotal GemFire and Apache Geode.
Still, even with PDX in play, users must take care not to cause the PDX serialized object on the servers in the cluster to be deserialized.
For example, consider a query on an object of the following Java type serialized as PDX…
@Region("People") class Person { private LocalDate birthDate; private String name; public int getAge() { // no explicit 'age' field/property in Person // age is just implemented in terms of the 'birthDate' field } }
And, the OQL query invokes a method on a Person
object…
SELECT * FROM /People p WHERE p.age >= 21
Then, this is going to cause a PDX serialized Person
object to be deserialized since age
is not a field of Person
,
but rather a method containing a computation based on another field of Person
(i.e. birthDate
).
Likewise, calling any java.lang.Object
method in a OQL query, like Object.toString()
, is going to cause
a deserialization to happen as well.
GemFire and Apache Geode do provide the read-serialized
configuration setting so that any cache Region.get(key)
operations that are potentially invoked inside a Function
do not cause PDX serialized objects to be deserialized. But, nothing will prevent an ill-conceived OQL query
from causing a deserialization, so be careful.
It is possible for Apache Geode/Pivotal Pivotal GemFire to support all 3 serialization formats simultaneously.
For instance, your application domain model might contain objects that implement the java.io.Serialiable
interface,
and you may be using a combination of the Data Serialization framework along with PDX.
![]() | Tip |
---|---|
While using Java Serialization with Data Serialization and PDX is possible, it is generally preferable and recommended that you use 1 serialization strategy. |
![]() | Note |
---|---|
Unlike Java Serialization, Data Serialization and PDX Serialization do not handle object graph cycles. |
More background on Apache Geode Serialization can be found here.
Previously, Spring Session for Pivotal GemFire/Apache Geode only supported Apache Geode/Pivotal GemFire’s Data Serialization format. The main motivation behind this was to take advantage of Delta Propagation since a Session’s state can be arbitrarily large.
However, as of Spring Session for Pivotal GemFire/Apache Geode 2.0, PDX is also supported and is now the new, default serialization option. The default was changed to PDX in Spring Session for Pivotal GemFire/Apache Geode 2.0 primarily because PDX is the most widely used and requested format by users.
PDX is certainly the most flexible format, so much so that you do not even need Spring Session for Pivotal GemFire/Apache Geode or any of its transitive dependencies on the classpath of the servers in the cluster to use Spring Session with either Apache Geode or Pivotal GemFire. In fact, with PDX, you do not even need to put your application domain object types that will be stored in the (HTTP) Session on the servers' classpath either.
Essentially, when using PDX serialization, Pivotal GemFire and Apache Geode do not require the associated Java types be present on the servers' classpath. So long as no deserialization happens on the servers in the cluster, you are safe.
The @EnableGemFireHttpSession
annotation introduces the new sessionSerializerBeanName
attribute that a user
can configure to refer to the name of a bean declared and registered in the Spring context that implements the desired
serialization strategy. The serialization strategy is used by Spring Session for Pivotal GemFire/Apache Geode to serialize
the Session state.
Out-of-the-box, Spring Session for Pivotal GemFire/Apache Geode provides 2 serialization strategies: 1 for PDX and 1 for Data Serialization. It automatically registers both serialization strategy beans in the Spring context. However, only 1 of those strategies is actually used at runtime… PDX!
The 2 beans registered in the Spring context implementing Data Serialization and PDX are named
SessionDataSerializer
and SessionPdxSerializer
, respectively.
By default, the sessionSerializerBeanName
attribute is set to SessionPdxSerializer
, as if the user annotated
his/her Spring Boot, Spring Session enabled application configuration class with…
@SpringBootApplication @EnableGemFireHttpSession(sessionSerializerBeanName = "SessionPdxSerializer") class MySpringSessionApplication { .. }
It is a simple matter to change the serialization strategy to Data Serialization instead by setting the
sessionSerializerBeanName
attribute to SessionDataSerializer
, like so…
@SpringBootApplication @EnableGemFireHttpSession(sessionSerializerBeanName = "SessionDataSerializer") class MySpringSessionApplication { .. }
Since these 2 values are so common, Spring Session for Pivotal GemFire/Apache Geode provides constants for each value in the
GemFireHttpSessionConfiguration
class: GemFireHttpSessionConfiguration.SESSION_PDX_SERIALIZER_BEAN_NAME
and GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME
. So, you could explicitly configure PDX
using…
import org.springframework.session.data.geode.config.annotation.web.http.GemFireHttpSessionConfiguration; @SpringBootApplication @EnableGemFireHttpSession(sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_PDX_SERIALIZER_BEAN_NAME) class MySpringSessionApplication { .. }
With 1 attribute and 2 provided bean definitions out-of-the-box, a user can specify which Serialization framework she wishes to use with her Spring Session application backed by either Apache Geode or Pivotal GemFire.
To abstract away the details of Apache Geode/Pivotal GemFire’s Data Serialization and PDX Serialization frameworks Spring Session for Pivotal GemFire/Apache Geode provides its own Serialization framework (facade) wrapping GemFire/Apache Geode’s Serialization frameworks.
The Serialization API exists under the org.springframework.session.data.gemfire.serialization
package.
The primary interface in this API is the…
org.springframework.session.data.gemfire.serialization.SessionSerializer
The interface is defined as…
Spring Session SessionSerializer
interface.
interface SessionSerializer<T, IN, OUT> { void serialize(T session, OUT out); T deserialize(IN in); boolean canSerializer(Class<?> type); boolean canSerializer(Object obj) { // call Object.getClass() in a null-safe way and then call and return canSerialize(:Class) } }
Basically, the interface allows you to serialize and deserialize a Spring Session
object.
The IN
and OUT
type parameters and corresponding method arguments of those types provide reference to the objects
responsible for writing the Session
to a stream of bytes or reading the Session
from a stream of bytes. The actual
arguments will be type dependent/specific, based on the underlying GemFire/Apache Geode Serialization strategy configured.
For instance, when using GemFire/Apache Geode’s PDX Serialization framework, IN
and OUT
will be instances of
org.apache.geode.pdx.PdxReader
and org.apache.geode.pdx.PdxWriter
, respectively. When GemFire/Apache Geode’s
Data Serialization framework has been configured, then IN
and OUT
will be instances of java.io.DataInput
and java.io.DataOuput
, respectively.
These arguments are provided to the SessionSerializer
implementation by the framework automatically, and as mentioned
above, is based on the underlying GemFire/Apache Geode Serialization strategy configured.
Essentially, even though Spring Session for Pivotal GemFire/Apache Geode provides a facade around GemFire/Apache Geode’s Serialization frameworks, under-the-hood, GemFire/Apache Geode still expects that one of these Serialization frameworks is being used to serialize data to/from GemFire/Apache Geode.
So what purpose does the SessionSerializer
interface really serve then?
Effectively, it allows a user to customize what aspects of the Session’s state actually gets serialized and stored
in Pivotal GemFire or Apache Geode. Application developers can provide their own custom, application-specific SessionSerializer
implementation, register it as a bean in the Spring context, and then configure it to be used by Spring Session
Data GemFire/Apache Geode to serialize the Session state…
@EnableGemFireHttpSession(sessionSerializerBeanName = "MyCustomSessionSerializer") class MySpringSessionDataGemFireApplication { @Bean("MyCustomSessionSerializer") SessionSerializer<Session, ?, ?> myCustomSessionSerializer() { ... } }
Spring Session for Pivotal GemFire/Apache Geode (SSDG) provides assistance when a user wants to implement a custom
SessionSerializer
that fits into one of Pivotal GemFire or Apache Geode’s Serialization frameworks.
If the user just implements the org.springframework.session.data.gemfire.serialization.SessionSerializer
interface
directly without extending from one of SSDG’s provided abstract base classes, pertaining to 1 of GemFire/Apache Geode’s
Serialization frameworks , then SSDG will wrap the user’s custom SessionSerializer
implementation in an instance of
org.springframework.session.data.gemfire.serialization.pdx.support.PdxSerializerSessionSerializerAdapter
and register
it with GemFire/Apache Geode as a org.apache.geode.pdx.PdxSerializer
.
Spring Session for Pivotal GemFire/Apache Geode is careful not to stomp on any existing, PdxSerializer
implementation that a user
may already have registered with the GemFire/Apache Geode cache by some other means. Indeed, several different and provided
implementations of the GemFire/Apache Geode org.apache.geode.pdx.PdxSerializer
interface do exists:
org.apache.geode.pdx.ReflectionBasedAutoSerializer
.
org.springframework.data.gemfire.mapping.MappingPdxSerializer
,
which is used in the SD Repository abstraction and SDG extension to handle mapping PDX serialized types to
the application domain object types defined in the application Repository interfaces.
This is accomplished by obtaining any currently registered PdxSerializer
instance on the cache and composing it
with the PdxSerializerSessionSerializerAdapter
wrapping the user’s custom application SessionSerializer
implementation and re-registering this "composite" PdxSerializer
on the GemFire/Apache Geode cache. The "composite"
PdxSerializer
implementation is provided Spring Session for Pivotal GemFire/Apache Geode’s
org.springframework.session.data.gemfire.pdx.support.ComposablePdxSerializer
class when entities are stored in either
GemFire or Apache Geode as PDX.
If no other PdxSerializer
was currently registered with the GemFire/Apache Geode cache, then the adapter
is simply registered.
Of course, the user is allowed to force the underlying GemFire/Apache Geode Serialization strategy used with his/her custom
SessionSerializer
implementation by doing 1 of the following…
SessionSerializer
implementation can implement GemFire/Apache Geode’s org.apache.geode.pdx.PdxSerializer
interface, or for convenience, extend Spring Session for Pivotal GemFire/Apache Geode’s
org.springframework.session.data.gemfire.serialization.pdx.AbstractPdxSerializableSessionSerializer
class
and Spring Session for Pivotal GemFire/Apache Geode will register the custom SessionSerializer
as a PdxSerializer
with GemFire/Apache Geode.
SessionSerializer
implementation can extend the GemFire/Apache Geode’s org.apache.geode.DataSerializable
class, or for convenience, extend Spring Session for Pivotal GemFire/Apache Geode’s
org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer
class
and Spring Session for Pivotal GemFire/Apache Geode will register the custom SessionSerializer
as a DataSerializer
with GemFire/Apache Geode.
SessionSerializer
implementation as before, not specifying which GemFire/Apache Geode
Serialization framework to use because the custom SessionSeriaizer
implementation does not implement any GemFire/Apache Geode
serialization interfaces or extend from any of Spring Session for Pivotal GemFire/Apache Geode’s provided abstract base classes,
and still have it registered in GemFire/Apache Geode as a DataSerializer
by declaring an additional
Spring Session for Pivotal GemFire/Apache Geode bean in the Spring context of type
org.springframework.session.data.gemfire.serialization.data.support.DataSerializerSessionSerializerAdapter
, like so…
Forcing the registration of a custom SessionSerializer as a DataSerializer in GemFire/Apache Geode.
@EnableGemFireHttpSession(sessionSerializerBeanName = "customSessionSerializer") class Application { @Bean DataSerializerSessionSerializerAdapter dataSerializerSessionSerializer() { return new DataSerializerSessionSerializerAdapter(); } @Bean SessionSerializer<Session, ?, ?> customSessionSerializer() { ... } }
Just by the very presence of the DataSerializerSessionSerializerAdapter
registered as a bean in the Spring context
any neutral, custom SessionSerializer
implementation will be treated and registered as a DataSerializer
in GemFire/Apache Geode.
Internally, Spring Session for Pivotal GemFire/Apache Geode maintains 2 representations for the (HTTP) Session and the Session’s attributes. Each representation is based on whether GemFire/Apache Geode "Deltas" are supported or not. GemFire/Apache Geode Delta Propagation is only enabled by Spring Session for Pivotal GemFire/Apache Geode when using Data Serialization for reasons that were discussed earlier.
Effectively, the strategy is:
DeltaCapableGemFireSession
and DeltaCapableGemFireSessionAttributes
representations are used.
GemFireSession
and GemFireSessionAttributes
representations are used.
It is possible to override these internal representations used by Spring Session for Pivotal GemFire/Apache Geode, and for users
to provide their own Session related types. The only strict requirement is that the Session implementation
must implement the org.springframework.session.Session
interface.
By way of example, let’s say the user wants to define their own Session implementation.
First, the user defines their Session
type. Perhaps the user’s custom Session
type even encapsulates and handles
the Session attributes without having to define a separate type.
User-defined Session interface implementation.
class MySession implements org.springframework.session.Session { ... }
Then, the user would need to extend the org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
class and override the createSession()
method to create instances of the user-defined Session
implementation class.
Custom SessionRepository implementation creating and returning instances of the custom Session type.
class MySessionRepository extends GemFireOperationsSessionRepository { @Override public Session createSession() { return new MySession(); } }
If the user provided his/her own custom SessionSerializer
implementation and GemFire/Apache Geode PDX Serialization
is configured, then the user is done.
However, if the user configured GemFire/Apache Geode Data Serialization then the user must provide a custom implementation
of the SessionSerializer
interface and either have it directly extend the GemFire/Apache Geode’s
org.apache.geode.DataSerializer
class, or extend Spring Session for Pivotal GemFire/Apache Geode’s
org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer
class
and override the getSupportedClasses():Class<?>[]
method.
For instance…
Custom SessionSerializer for custom Session type.
class MySessionSerializer extends AbstractDataSerializableSessionSerializer { @Override public Class<?>[] getSupportedClasses() { return new Class[] { MySession.class }; } ... }
Unfortunately, getSupportedClasses()
cannot return the generic Spring Session org.springframework.session.Session
interface type. If it could then we could avoid the explicit need to override the getSupportedClasses()
method
on the custom DataSerializer
implementaton. Bu, GemFire/Apache Geode’s Data Serialization framework can only match
on exact class types since it incorrectly and internally stores and refers to the class type by name, which basically
requires a user to override and implement the getSupportedClasses()
method.
Fortunately, both javax.servlet.http.HttpSession
and javax.servlet.http.HttpServletRequest
(the API for
obtaining an HttpSession
) are interfaces. This means we can provide our own implementations for each of these APIs.
![]() | Note |
---|---|
This section describes how Spring Session provides transparent integration with |
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:
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { public SessionRepositoryRequestWrapper(HttpServletRequest original) { super(original); } public HttpSession getSession() { return getSession(true); } public HttpSession getSession(boolean createNew) { // create an HttpSession implementation from Spring Session } // ... other methods delegate to the original HttpServletRequest ... }
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 javax.servlet.http.HttpServletRequest
implementation using a Servlet Filter
called SessionRepositoryFilter
.
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); chain.doFilter(customRequest, response, chain); } // ... }
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
.
Spring Session supports HttpSessionListener
by translating SessionDestroyedEvent
and SessionCreatedEvent
into
HttpSessionEvent
by declaring SessionEventHttpSessionListenerAdapter
.
To use this support, you need to:
SessionRepository
implementation supports and is configured to fire SessionDestroyedEvent
and SessionCreatedEvent
.
SessionEventHttpSessionListenerAdapter
as a Spring bean.
HttpSessionListener
into the SessionEventHttpSessionListenerAdapter
If you are using the configuration support documented in HttpSession with Apache Geode,
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.
A SessionRepository
is in charge of creating, persisting and retrieving Session
instances and state.
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.
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 Session’s Apache Geode support implements FindByIndexNameSessionRepository
.
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 |
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.
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 Apache Geode specific implementation of the SessionRepository
interface, the GemFireOperationsSessionRepository
.
GemFireOperationsSessionRepository
is a SessionRepository
implementation that is implemented using Spring Session for Apache 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
.
While best practices concerning the proper definition of Indexes that positively impact Apache Geode’s performance is beyond the scope of this document, it is important to realize that Spring Session for Apache Geode creates and uses Indexes to query and find Sessions efficiently.
Out-of-the-box, Spring Session for Apache 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:
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
session.setAttribute(indexName, username);
Map<String, Session> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, username);
Alternatively, Spring Session for Apache 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:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
Map<String, Session> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, authentication.getName());
This enables developers using the GemFireOperationsSessionRepository
programmatically to query and find all Sessions
with a given principal name efficiently.
Additionally, Spring Session for Apache 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 Apache 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’s support for HttpSession
backed by Apache Geode.
String indexName = "name1"; session.setAttribute(indexName, indexValue); Map<String, Session> idToSessions = this.sessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
![]() | Note |
---|---|
Only Session attribute names identified in the |
However, there is one caveat. Any values stored in indexable Session attributes must implement the java.lang.Comparable<T>
interface. If those object values do not implement Comparable
, then Pivotal GemFire will throw an error on startup when the
Index is defined for Regions with persistent Session data, or when an attempt is made at runtime to assign the indexable
Session attribute a value that is not Comparable
and the Session is saved to GemFire.
![]() | Note |
---|---|
Any Session attribute that is not indexed may store non- |
To learn more about GemFire’s Range-based Indexes, see Creating Indexes on Map Fields.
To learn more about Pivotal GemFire Indexing in general, see Working with Indexes.
We are glad to consider you a part of our community. Please find additional information below.
You can get help by asking questions on StackOverflow with the tag spring-session. Similarly we encourage helping others by answering questions on StackOverflow.
The source code can be found on GitHub at https://github.com/spring-projects/spring-session-data-geode
We track issues in GitHub Issues at https://github.com/spring-projects/spring-session-data-geode/issues
We appreciate Pull Requests.
Spring Session for Apache Geode and Spring Session for Pivotal GemFire are Open Source Software released under the Apache 2.0 license.
The minimum requirements for Spring Session are:
@EnableGemFireHttpSession
requires Spring Data Apache 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 |