Remove GemFire

Fixes gh-768
This commit is contained in:
Rob Winch
2017-04-26 19:26:14 -05:00
parent b254c7c6b9
commit 02da23a2a0
81 changed files with 0 additions and 13496 deletions

View File

@@ -1,70 +0,0 @@
package build;
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.TaskAction
public class GemFireServerPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.tasks.create('gemFireServer', GemFireServerTask)
project.tasks.integrationTest.doLast {
println 'Stopping GemFire Server...'
project.tasks.gemFireServer.process?.destroyForcibly()
}
project.tasks.prepareAppServerForIntegrationTests {
dependsOn project.tasks.gemFireServer
doFirst {
project.gretty {
jvmArgs = ["-Dspring.session.data.gemfire.port=${project.tasks.gemFireServer.port}"]
}
}
}
}
static int availablePort() {
new ServerSocket(0).withCloseable { socket ->
socket.localPort
}
}
static class GemFireServerTask extends DefaultTask {
def mainClassName = "sample.ServerConfig"
def process
def port
boolean debug
@TaskAction
def greet() {
port = availablePort()
println "Starting GemFire Server on port [$port]..."
def out = debug ? System.out : new StringBuilder()
def err = debug ? System.out : 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[] 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}",
'sample.ServerConfig'
]
//println commandLine
project.tasks.appRun.ext.process = process = commandLine.execute()
process.consumeProcessOutput(out, err)
}
}
}

View File

@@ -1 +0,0 @@
implementation-class=build.GemFireServerPlugin

View File

@@ -3,7 +3,6 @@ apply plugin: 'io.spring.convention.spring-test'
dependencies {
testCompile project(':spring-session')
testCompile project(':spring-session-data-gemfire')
testCompile project(':spring-session-data-mongo')
testCompile project(':spring-session-data-redis')
testCompile "org.springframework:spring-jdbc"

View File

@@ -1,270 +0,0 @@
= Spring Session - HttpSession with GemFire Client/Server using Spring Boot
John Blum
:toc:
This guide describes how to build a _Spring Boot_ application configured with _Spring Session_ to transparently leverage
Pivotal GemFire to back a web application's `HttpSession`.
In this sample, GemFire's client/server topology is employed using a pair of _Spring Boot_ applications, one to
configure and run a GemFire Server and another to configure and run the client, Spring MVC-based web application
making use of the `HttpSession`.
NOTE: The completed guide can be found in the <<httpsession-gemfire-boot-sample,HttpSession with GemFire using Spring Boot Sample Application>>.
== Updating Dependencies
Before using _Spring Session_, you must ensure that the required dependencies are included.
If you are using Maven, include the following `dependencies` in your _pom.xml_:
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-gemfire</artifactId>
<version>{spring-session-version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
----
ifeval::["{version-snapshot}" == "true"]
Since we are using a SNAPSHOT version, we need to add the Spring Snapshot Maven Repository.
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
.pom.xml
[source,xml]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
----
endif::[]
ifeval::["{version-milestone}" == "true"]
Since we are using a Milestone version, we need to add the Spring Milestone Maven Repository.
If you are using Maven, include the following `repository` declaration in your _pom.xml_:
.pom.xml
[source,xml]
----
<repositories>
<!-- ... -->
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
</repositories>
----
endif::[]
// tag::config[]
[[httpsession-spring-java-configuration]]
== Spring Boot Configuration
After adding the required dependencies and repository declarations, we can create our Spring configuration
for both the GemFire client and server using _Spring Boot_. The Spring configuration is responsible for
creating a Servlet Filter that replaces the `HttpSession` with an implementation backed by _Spring Session_
and GemFire.
=== Spring Boot-based GemFire Server
We start with the _Spring Boot_ application for configuring and bootstrapping a GemFire Server process...
[source,java]
----
include::{samples-dir}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.
The sample also makes use of a `PropertySourcesPlaceholderConfigurer` bean in order to externalize the sample application
configuration to affect GemFire and application configuration/behavior from the command-line (e.g. such as GemFire's
`log-level` using the `gemfire.log.level` System property; more details below).
=== Spring Boot-based GemFire cache client Web application
Now, we create 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.
[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".
There are many other utility methods, so please refer to the actual source code for full details.
TIP: In typical GemFire deployments, where the cluster includes potentially hundreds or thousands of GemFire data nodes
(servers), it is more common for clients to connect to one or more GemFire Locators running in the cluster. A Locator
passes meta-data to clients about the servers available, their load and which servers have the client's data of interest,
which is particularly important in direct, single-hop data access and latency-sensitive operations. See more details
about the http://gemfire.docs.pivotal.io/docs-gemfire/latest/topologies_and_comm/cs_configuration/chapter_overview.html[Client/Server Topology in GemFire's User Guide].
NOTE: For more information on configuring _Spring Data GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
The `@EnableGemFireHttpSession` annotation enables a developer to configure certain aspects of both _Spring Session_
and GemFire out-of-the-box using the following attributes:
* `maxInactiveIntervalInSeconds` - controls _HttpSession_ idle-timeout expiration (defaults to **30 minutes**).
* `regionName` - specifies the name of the GemFire Region used to store `HttpSession` state (defaults is "*ClusteredSpringSessions*").
* `clientRegionShort` - specifies GemFire's http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policy]
with a GemFire http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/client/ClientRegionShortcut.html[ClientRegionShortcut]
(default is `PROXY`). This attribute is only used when configuring client Region.
* `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 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.
[[httpsession-gemfire-boot-sample]]
== HttpSession with GemFire using Spring Boot Sample Application
=== Running the httpsession-gemfire-boot Sample Application
You can run the sample by obtaining the {download-url}[source code] and invoking the following commands.
First, you must run the server:
----
$ ./gradlew :samples:httpsession-gemfire-boot:run [-Dgemfire.log-level=config]
----
Then, in a separate terminal, run the client:
----
$ ./gradlew :samples:httpsession-gemfire-boot:bootRun [-Dgemfire.log-level=config]
----
You should now be able to access the application at http://localhost:8080/. In this sample, the web application
is the client cache and the server is standalone.
=== Exploring the httpsession-gemfire-boot Sample Application
Try using the application. Fill out the form with the following information:
* **Attribute Name:** _username_
* **Attribute Value:** _test_
Now click the **Set Attribute** button. You should now see the attribute name and value displayed in the table
along with an additional attribute (`requestCount`) indicating the number of Session interactions (via HTTP requests).
=== How does it work?
We interact with the standard `HttpSession` in the the Spring MVC web service endpoint, shown here for convenience:
.src/main/java/sample/SessionServlet.java
[source,java]
----
@RequestMapping(method = RequestMethod.POST, path = "/session")
public String session(HttpSession session, ModelMap modelMap,
@RequestParam(name = "attributeName", required = false) String name,
@RequestParam(name = "attributeValue", required = false) String value) {
modelMap.addAttribute("sessionAttributes",
attributes(setAttribute(updateRequestCount(session), name, value)));
return INDEX_TEMPLATE_VIEW_NAME;
}
----
Instead of using the embedded HTTP server's `HttpSession`, we are actually persisting the Session state in GemFire.
_Spring Session_ creates a cookie named SESSION in your browser that contains the id of your session.
Go ahead and view the cookies (click for help with https://developer.chrome.com/devtools/docs/resources#cookies[Chrome]
or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
NOTE: The following instructions assume you have a local GemFire installation. For more information on installation,
see http://gemfire.docs.pivotal.io/docs-gemfire/latest/getting_started/installation/install_intro.html[Installing Pivotal GemFire].
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`. For example, on a Linux-based system type the following
at the command-line:
$ gfsh
Then, enter the following commands in _Gfsh_ ensuring to replace `70002719-3c54-4c20-82c3-e7faa6b718f3` with the value
of your SESSION cookie, or the session ID returned by the GemFire OQL query (which should match):
....
gfsh>connect --jmx-manager=localhost[1099]
gfsh>query --query='SELECT * FROM /ClusteredSpringSessions.keySet'
Result : true
startCount : 0
endCount : 20
Rows : 1
Result
------------------------------------
70002719-3c54-4c20-82c3-e7faa6b718f3
NEXT_STEP_NAME : END
gfsh>remove --region=/ClusteredSpringSessions --key="70002719-3c54-4c20-82c3-e7faa6b718f3"
....
NOTE: The _GemFire User Guide_ has more detailed instructions on using http://gemfire.docs.pivotal.io/docs-gemfire/latest/tools_modules/gfsh/chapter_overview.html[gfsh].
Now visit the application at http://localhost:8080/ again and observe that the attribute we added is no longer displayed.
Alternatively, you can wait **20 seconds** for the session to expire and timeout, and then refresh the page. The attribute
we added should no longer be displayed in the table.

View File

@@ -1,272 +0,0 @@
= 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.

View File

@@ -1,209 +0,0 @@
= Spring Session - HttpSession with GemFire P2P (Quick Start)
John Blum, Rob Winch
: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.
NOTE: The completed guide can be found in the <<httpsession-gemfire-p2p-java-sample-app,HttpSession with GemFire (P2P) 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-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.
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 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 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://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/RegionShortcut.html[RegionShortcut]
(default is `PARTITION`).
NOTE: `clientRegionShort` is ignored in a peer cache configuration and only applies when a client-server topology,
and more specifically, a GemFire client cache is used.
== 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 `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.
You can find an example below:
.src/main/java/sample/Initializer.java
[source,java]
----
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`.
<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`.
// end::config[]
[[httpsession-gemfire-p2p-java-sample-app]]
== HttpSession with GemFire (P2P) Sample Application
=== Running the httpsession-gemfire-p2p 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]
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the httpsession-gemfire-p2p 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-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.
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 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):
....
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/ and observe that the attribute we added is no longer displayed.

View File

@@ -1,273 +0,0 @@
= Spring Session - HttpSession with GemFire Client/Server using XML (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 XML Configuration.
NOTE: The completed guide can be found in the <<httpsession-gemfire-clientserver-xml-sample-app,HttpSession with GemFire (Client/Server) using XML 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-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.
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 `<context:annotation-config/>` element so that any
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
by Spring will be configured appropriately.
<2> The `META-INF/spring/application.properties` file are used along with the `PropertySourcesPlaceholderConfigurer`
bean to replace placeholders in the Spring XML configuration meta-data with the approrpriate property values.
<3> Then the `GemFireCacheSeverReadyBeanPostProcessor`is registered to determine whether a GemFire Server
at the designated host/port is running and listening for client connections, blocking client startup until
the server is available and ready.
<4> Next, we include a `Properties` bean to configure certain aspects of the GemFire client cache using
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.
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.
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].
=== 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.
In this sample, we will use the following GemFire Server Java Configuration:
[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 `<context:annotation-config>` element so that any
Spring beans declared in the XML config that are annotated with either Spring or Standard Java annotations supported
by Spring will be configured appropriately.
<2> 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.
The GemFire Server configuration gets bootstrapped with the following:
[source,java]
----
include::{samples-dir}xml/gemfire-clientserver/src/main/java/sample/Application.java[tags=class]
----
TIP: Instead of a simple Java class with a main method, you could also use _Spring Boot_.
<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.
== XML Servlet Container Initialization
Our <<httpsession-spring-xml-configuration,Spring XML Configuration>> created a Spring bean named `springSessionRepositoryFilter`
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with
a custom implementation that is backed by Spring Session and GemFire.
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
[source,xml,indent=0]
----
include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/web.xml[tags=context-param]
include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/web.xml[tags=listeners]
----
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener]
reads the `contextConfigLocation` 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
[source,xml,indent=0]
----
include::{samples-dir}xml/gemfire-clientserver/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
----
The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy]
will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy`
is invoked, the `springSessionRepositoryFilter` will be invoked.
// end::config[]
[[httpsession-gemfire-clientserver-xml-sample-app]]
== HttpSession with GemFire (Client/Server) using XML Sample Application
=== Running the httpsession-gemfire-clientserver-xml 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-xml:run [-Dsample.httpsession.gemfire.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]
----
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-xml 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}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.
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 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.

View File

@@ -1,210 +0,0 @@
= Spring Session - HttpSession with GemFire P2P using XML (Quick Start)
John Blum, Rob Winch
: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.
NOTE: The completed guide can be found in the <<httpsession-gemfire-p2p-xml-sample-app,HttpSession with GemFire (P2P) using XML 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-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.
Add the following Spring Configuration:
.src/main/webapp/WEB-INF/spring/session.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
----
<1> We use the combination of `<context:annotation-config/>` 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.
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 GemFire_, refer to the http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/[reference guide].
== XML Servlet Container Initialization
Our <<httpsession-spring-xml-configuration,Spring XML Configuration>> created a Spring bean named `springSessionRepositoryFilter`
that implements `Filter`. The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with
a custom implementation that is backed by Spring Session and GemFire.
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
[source,xml,indent=0]
----
include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/web.xml[tags=context-param]
include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/web.xml[tags=listeners]
----
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener]
reads the `contextConfigLocation` 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.
The following snippet performs this last step for us:
.src/main/webapp/WEB-INF/web.xml
[source,xml,indent=0]
----
include::{samples-dir}xml/gemfire-p2p/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
----
The http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy]
will look up a bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`. For every request that `DelegatingFilterProxy`
is invoked, the `springSessionRepositoryFilter` will be invoked.
// end::config[]
[[httpsession-gemfire-p2p-xml-sample-app]]
== HttpSession with GemFire (P2P) using XML Sample Application
=== Running the httpsession-gemfire-p2p-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]
----
You should now be able to access the application at http://localhost:8080/
=== Exploring the httpsession-gemfire-p2p-xml 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}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.
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 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):
....
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/ and observe that the attribute we added is no longer displayed.

View File

@@ -52,10 +52,6 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis.
| link:guides/boot.html[HttpSession with Redis 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/mongo[HttpSession with Mongo]
| Demonstrates how to use Spring Session to replace the `HttpSession` with Mongo.
| link:guides/boot-mongo.html[HttpSession with Mongo Guide]
@@ -86,14 +82,6 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis.
| link:guides/java-redis.html[HttpSession with Redis 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-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/jdbc[HttpSession with JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/java-jdbc.html[HttpSession with JDBC Guide]
@@ -128,14 +116,6 @@ If you are looking to get started with Spring Session, the best place to start i
| Demonstrates how to use Spring Session to replace the `HttpSession` with a Redis store.
| link:guides/xml-redis.html[HttpSession with Redis 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-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/jdbc[HttpSession with JDBC]
| Demonstrates how to use Spring Session to replace the `HttpSession` with a relational database store.
| link:guides/xml-jdbc.html[HttpSession with JDBC Guide]
@@ -200,110 +180,6 @@ You can read the basic steps for integration below, but you are encouraged to fo
include::guides/xml-redis.adoc[tags=config,leveloffset=+3]
[[httpsession-gemfire]]
=== HttpSession with Pivotal GemFire
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 GemFire and conveniently accessed
with Spring Session's API.
The two most common topologies to manage Spring Sessions using GemFire include:
* <<httpsession-gemfire-clientserver,Client-Server>>
* <<httpsession-gemfire-p2p,Peer-To-Peer (P2P)>>
Additionally, GemFire supports site-to-site replication using http://gemfire.docs.pivotal.io/docs-gemfire/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN functionality].
The ability to configure and use GemFire's WAN support is independent of Spring Session, and is beyond the scope
of this document. More details on GemFire WAN functionality can be found http://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:gateway[here].
[[httpsession-gemfire-clientserver]]
==== GemFire 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
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.
You can configure a Client-Server topology with either:
* <<httpsession-gemfire-clientserver-java,Java-based Configuration>>
* <<httpsession-gemfire-clientserver-xml,XML-based Configuration>>
[[httpsession-gemfire-clientserver-java]]
===== GemFire Client-Server Java-based Configuration
This section describes how to use GemFire's Client-Server topology to back an `HttpSession` with Java-based configuration.
NOTE: The <<samples,HttpSession with GemFire (Client-Server) Sample>> 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.
include::guides/java-gemfire-clientserver.adoc[tags=config,leveloffset=+3]
[[http-session-gemfire-clientserver-xml]]
===== GemFire Client-Server XML-based Configuration
This section describes how to use GemFire's Client-Server topology to back an `HttpSession` with XML-based configuration.
NOTE: The <<samples,HttpSession with GemFire (Client-Server) using XML Sample>> 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.
include::guides/xml-gemfire-clientserver.adoc[tags=config,leveloffset=+3]
[[httpsession-gemfire-p2p]]
==== GemFire 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,
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.
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.
You can configure a Peer-To-Peer (P2P) topology with either:
* <<httpsession-gemfire-p2p-java,Java-based Configuration>>
* <<httpsession-gemfire-p2p-xml,XML-based Configuration>>
[[httpsession-gemfire-p2p-java]]
===== GemFire 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.
NOTE: The <<samples, HttpSession with GemFire (P2P) Sample>> 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.
include::guides/java-gemfire-p2p.adoc[tags=config,leveloffset=+3]
[[httpsession-gemfire-p2p-xml]]
===== GemFire 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.
NOTE: The <<samples, HttpSession with GemFire (P2P) using XML Sample>> 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.
include::guides/xml-gemfire-p2p.adoc[tags=config,leveloffset=+3]
[[httpsession-jdbc]]
=== HttpSession with JDBC
@@ -952,89 +828,6 @@ redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed
"\xac\xed\x00\x05t\x00\x03rob"
----
[[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`.
[[api-gemfireoperationssessionrepository-indexing]]
==== Using Indexes with GemFire
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
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:
[source,java,indent=0]
----
include::{docs-itest-dir}docs/http/HttpSessionGemFireIndexingITests.java[tags=findbyindexname-set]
include::{docs-itest-dir}docs/http/HttpSessionGemFireIndexingITests.java[tags=findbyindexname-get]
----
[[api-gemfireoperationssessionrepository-indexing-security]]
==== Using Indexes with GemFire & 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:
[source,java,indent=0]
----
include::{docs-itest-dir}docs/http/HttpSessionGemFireIndexingITests.java[tags=findbyspringsecurityindexname-context]
include::{docs-itest-dir}docs/http/HttpSessionGemFireIndexingITests.java[tags=findbyspringsecurityindexname-get]
----
[[api-gemfireoperationssessionrepository-indexing-custom]]
==== Using Custom Indexes with GemFire
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
`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.
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.
For example, the following configuration:
[source,java,indent=0]
----
include::{docs-itest-dir}docs/http/gemfire/indexablesessionattributes/GemFireHttpSessionConfig.java[tags=class-start]
// ...
}
----
will allow searching for sessions using the following:
[source,java,indent=0]
----
include::{docs-itest-dir}docs/http/gemfire/indexablesessionattributes/HttpSessionGemFireIndexingCustomITests.java[tags=findbyindexname-set]
include::{docs-itest-dir}docs/http/gemfire/indexablesessionattributes/HttpSessionGemFireIndexingCustomITests.java[tags=findbyindexname-get]
----
NOTE: Only Session attribute names identified in the `@EnableGemFireHttpSession` annotation's `indexableSessionAttributes`
attribute will have an Index defined. All other Session attributes will not be indexed.
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 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-`Comparable` values.
To learn more about GemFire's Range-based Indexes, see http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/creating_map_indexes.html[Creating Indexes on Map Fields].
To learn more about GemFire Indexing in general, see http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/query_index.html[Working with Indexes].
[[api-mapsessionrepository]]
=== MapSessionRepository

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
* @author John Blum
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = HttpSessionGemFireIndexingITests.Config.class)
public class HttpSessionGemFireIndexingITests {
@Autowired
private GemFireOperationsSessionRepository sessionRepository;
@Test
public void findByIndexName() {
ExpiringSession session = this.sessionRepository.createSession();
String username = "HttpSessionGemFireIndexingITests-findByIndexName-username";
// tag::findbyindexname-set[]
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
session.setAttribute(indexName, username);
// end::findbyindexname-set[]
this.sessionRepository.save(session);
// tag::findbyindexname-get[]
Map<String, ExpiringSession> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, username);
// end::findbyindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());
this.sessionRepository.delete(session.getId());
}
@Test
@WithMockUser("HttpSessionGemFireIndexingITests-findBySpringSecurityIndexName")
public void findBySpringSecurityIndexName() {
ExpiringSession session = this.sessionRepository.createSession();
// tag::findbyspringsecurityindexname-context[]
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
// end::findbyspringsecurityindexname-context[]
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);
this.sessionRepository.save(session);
// tag::findbyspringsecurityindexname-get[]
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
Map<String, ExpiringSession> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, authentication.getName());
// end::findbyspringsecurityindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());
this.sessionRepository.delete(session.getId());
}
@PeerCacheApplication(name = "HttpSessionGemFireIndexingITests", logLevel = "error")
@EnableGemFireHttpSession(regionName = "HttpSessionGemFireIndexingTestRegion")
static class Config {
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.http.gemfire.indexablesessionattributes;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
* @author John Blum
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = HttpSessionGemFireIndexingCustomITests.Config.class)
public class HttpSessionGemFireIndexingCustomITests {
@Autowired
private GemFireOperationsSessionRepository sessionRepository;
@Test
public void findByIndexName() {
ExpiringSession session = this.sessionRepository.createSession();
String attrValue = "HttpSessionGemFireIndexingCustomITests-findByIndexName";
// tag::findbyindexname-set[]
String indexName = "name1";
session.setAttribute(indexName, attrValue);
// end::findbyindexname-set[]
this.sessionRepository.save(session);
// tag::findbyindexname-get[]
Map<String, ExpiringSession> idToSessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, attrValue);
// end::findbyindexname-get[]
assertThat(idToSessions.keySet()).containsOnly(session.getId());
this.sessionRepository.delete(session.getId());
}
@PeerCacheApplication(name = "HttpSessionGemFireIndexingCustomITests", logLevel = "error")
@EnableGemFireHttpSession(indexableSessionAttributes = { "name1", "name2", "name3" },
regionName = "HttpSessionGemFireIndexingCustomTestRegion")
static class Config {
}
}

View File

@@ -13,9 +13,6 @@ configurations.spring3TestRuntime {
if (details.requested.name == 'spring-data-commons') {
details.useVersion '1.12.1.RELEASE'
}
if (details.requested.name == 'spring-data-gemfire') {
details.useVersion '1.8.1.RELEASE'
}
if (details.requested.name == 'spring-data-keyvalue') {
details.useVersion '1.1.1.RELEASE'
}

View File

@@ -1,87 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
apply plugin: "application"
dependencies {
compile project(':spring-session-data-gemfire')
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.webjars:bootstrap"
compile "org.webjars:webjars-locator"
runtime "org.springframework.shell:spring-shell"
testCompile "org.springframework.boot:spring-boot-starter-test"
integrationTestCompile seleniumDependencies
integrationTestRuntime "org.springframework.shell:spring-shell"
}
run {
doFirst {
mainClassName = 'sample.server.GemFireServer'
}
}
bootJar {
mainClass = 'sample.client.Application'
}
task runGemFireServer() {
doLast {
ext.port = reservePort()
println "Starting GemFire Server on port [$port] ..."
def out = new StringBuilder()
def err = new StringBuilder()
String classpath = sourceSets.main.runtimeClasspath.collect { it }.join(File.pathSeparator)
String[] commandLine = [
'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'),
'sample.server.GemFireServer'
]
//println commandLine
ext.process = commandLine.execute()
//ext.process = new ProcessBuilder().command(commandLine).redirectErrorStream(true).start();
ext.process.consumeProcessOutput(out, err)
//println 'OUT: ' + out
//println 'ERR: ' + err
}
}
integrationTest {
dependsOn runGemFireServer
doFirst {
def port = reservePort()
systemProperties['management.port'] = 0
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")
}
doLast {
println 'Stopping GemFire Server...'
runGemFireServer.process?.destroyForcibly()
}
}
def reservePort() {
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2014-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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;
import sample.client.Application;
import sample.pages.HomePage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class AttributeTests {
@Autowired
private MockMvc mockMvc;
private WebDriver driver;
@Before
public void setup() {
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void noAttributes() {
HomePage home = HomePage.go(this.driver, HomePage.class);
assertThat(home.attributes().size()).isEqualTo(0);
}
@Test
public void createAttribute() {
HomePage home = HomePage.go(this.driver, HomePage.class);
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
assertThat(home.attributes()).extracting("attributeName").containsOnly("requestCount", "a");
assertThat(home.attributes()).extracting("attributeValue").containsOnly("1", "b");
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2014-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.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage {
private WebDriver driver;
@FindBy(tagName = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<Attribute>();
}
private static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
driver.get(baseUrl + get);
}
public static <T> T go(WebDriver driver, Class<T> page) {
get(driver, "/");
return PageFactory.initElements(driver, page);
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public HomePage logout() {
WebElement logout = this.driver
.findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(this.driver, HomePage.class);
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<Attribute>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,336 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import 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.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.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* A Spring Boot, GemFire cache client, web application that reveals the current state of the HTTP Session.
*
* @author John Blum
* @see javax.servlet.http.HttpSession
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.context.annotation.Bean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.stereotype.Controller
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @since 1.2.1
*/
// tag::class[]
@SpringBootApplication
@EnableGemFireHttpSession(poolName = "DEFAULT")// <1>
@Controller
public class Application {
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 INDEX_TEMPLATE_VIEW_NAME = "index";
static final String PING_RESPONSE = "PONG";
static final String REQUEST_COUNT_ATTRIBUTE_NAME = "requestCount";
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@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:12480}") int port) { // <3>
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
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<Pool> defaultPool = new AtomicReference<Pool>(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;
}
};
}
@RequestMapping("/")
public String index() { // <6>
return INDEX_TEMPLATE_VIEW_NAME;
}
@RequestMapping(method = RequestMethod.GET, path = "/ping")
@ResponseBody
public String ping() { // <7>
return PING_RESPONSE;
}
@RequestMapping(method = RequestMethod.POST, path = "/session")
public String session(HttpSession session, ModelMap modelMap,
@RequestParam(name = "attributeName", required = false) String name,
@RequestParam(name = "attributeValue", required = false) String value) { // <8>
modelMap.addAttribute("sessionAttributes", attributes(setAttribute(updateRequestCount(session), name, value)));
return INDEX_TEMPLATE_VIEW_NAME;
}
// end::class[]
/* (non-Javadoc) */
@SuppressWarnings("all")
HttpSession updateRequestCount(HttpSession session) {
synchronized (session) {
Integer currentRequestCount = (Integer) session.getAttribute(REQUEST_COUNT_ATTRIBUTE_NAME);
session.setAttribute(REQUEST_COUNT_ATTRIBUTE_NAME, nullSafeIncrement(currentRequestCount));
return session;
}
}
/* (non-Javadoc) */
Integer nullSafeIncrement(Integer value) {
return (nullSafeIntValue(value) + 1);
}
/* (non-Javadoc) */
int nullSafeIntValue(Number value) {
return (value != null ? value.intValue() : 0);
}
/* (non-Javadoc) */
HttpSession setAttribute(HttpSession session, String attributeName, String attributeValue) {
if (isSet(attributeName, attributeValue)) {
session.setAttribute(attributeName, attributeValue);
}
return session;
}
/* (non-Javadoc) */
boolean isSet(String... values) {
boolean set = true;
for (String value : values) {
set &= StringUtils.hasText(value);
}
return set;
}
/* (non-Javadoc) */
Map<String, String> attributes(HttpSession session) {
Map<String, String> sessionAttributes = new HashMap<String, String>();
for (String attributeName : toIterable(session.getAttributeNames())) {
sessionAttributes.put(attributeName, String.valueOf(session.getAttribute(attributeName)));
}
return sessionAttributes;
}
/* (non-Javadoc) */
<T> Iterable<T> toIterable(final Enumeration<T> enumeration) {
return new Iterable<T>() {
public Iterator<T> iterator() {
return (enumeration == null ? Collections.<T>emptyIterator()
: CollectionUtils.toIterator(enumeration));
}
};
}
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.server;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
/**
* A Spring Boot application bootstrapping a GemFire Cache Server JVM process.
*
* @author John Blum
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.context.annotation.Bean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.apache.geode.cache.Cache
* @since 1.2.1
*/
// tag::class[]
@SpringBootApplication
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 20) // <1>
public class GemFireServer {
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(GemFireServer.class);
springApplication.setWebEnvironment(false);
springApplication.run(args);
}
@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>
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache 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:12480}") int port) { // <4>
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
gemfireCacheServer.setAutoStartup(true);
gemfireCacheServer.setBindAddress(bindAddress);
gemfireCacheServer.setCache(gemfireCache);
gemfireCacheServer.setHostNameForClients(hostnameForClients);
gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
gemfireCacheServer.setPort(port);
return gemfireCacheServer;
}
}
// end::class[]

View File

@@ -1,186 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.server;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.server.CacheServer;
import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.util.StringUtils;
/**
* The {@link NativeGemFireServer} class uses the GemFire API to create a GemFire (cache) instance.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.server.CacheServer
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @since 1.3.0
*/
@SuppressWarnings("unused")
public final class NativeGemFireServer implements Runnable {
private static final int GEMFIRE_CACHE_SERVER_PORT =
Integer.getInteger("spring-session-data-gemfire.cache.server.port", 12480);
private static final String GEMFIRE_CACHE_SERVER_HOST = "localhost";
private static final String GEMFIRE_CACHE_SERVER_HOSTNAME_FOR_CLIENTS = GEMFIRE_CACHE_SERVER_HOST;
private static final String GEMFIRE_LOG_FILENAME_PATTERN =
String.format("%s", NativeGemFireServer.class.getSimpleName()).concat("-%s.log");
public static void main(String[] args) {
newNativeGemFireServer(args).run();
}
private final String[] args;
private static File newGemFireLogFile(String suffix) {
return new File(String.format(GEMFIRE_LOG_FILENAME_PATTERN, suffix));
}
private static NativeGemFireServer newNativeGemFireServer(String[] args) {
return new NativeGemFireServer(args);
}
private static String[] nullSafeStringArray(String[] array) {
return (array != null ? array.clone() : new String[0]);
}
private static void writeStringTo(File file, String fileContents) {
PrintWriter fileWriter = null;
try {
fileWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, true)), true);
fileWriter.println(fileContents);
fileWriter.flush();
}
catch (IOException e) {
throw new RuntimeException(String.format("Failed to write [%s] to file [%s]", fileContents, file), e);
}
finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
private NativeGemFireServer(String[] args) {
this.args = nullSafeStringArray(args);
}
/**
* @inheritDoc
*/
public void run() {
run(this.args);
}
private void run(String[] args) {
try {
writeStringTo(newGemFireLogFile("stdout"), "Before");
registerShutdownHook(addCacheServer(createRegion(gemfireCache(
gemfireProperties(applicationName())))));
writeStringTo(newGemFireLogFile("stdout"), "After");
}
catch (Throwable e) {
writeStringTo(newGemFireLogFile("stderr"), e.toString());
}
}
private String applicationName() {
return applicationName(null);
}
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);
gemfireProperties.setProperty("log-file", "gemfire-server.log");
gemfireProperties.setProperty("log-level", "config");
//gemfireProperties.setProperty("jmx-manager", "true");
//gemfireProperties.setProperty("jmx-manager-start", "true");
return gemfireProperties;
}
private Cache gemfireCache(Properties gemfireProperties) {
return new CacheFactory(gemfireProperties).create();
}
private Cache createRegion(Cache gemfireCache) {
RegionFactory<Object, AbstractGemFireOperationsSessionRepository.GemFireSession> regionFactory =
gemfireCache.createRegionFactory(RegionShortcut.PARTITION);
regionFactory.setKeyConstraint(Object.class);
regionFactory.setValueConstraint(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
regionFactory.setStatisticsEnabled(true);
regionFactory.setEntryIdleTimeout(newExpirationAttributes(1800, ExpirationAction.INVALIDATE));
Region region = regionFactory.create(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
return gemfireCache;
}
private ExpirationAttributes newExpirationAttributes(int expirationTime, ExpirationAction expirationAction) {
return new ExpirationAttributes(expirationTime, expirationAction);
}
private Cache addCacheServer(Cache gemfireCache) throws IOException {
CacheServer cacheServer = gemfireCache.addCacheServer();
cacheServer.setBindAddress(GEMFIRE_CACHE_SERVER_HOST);
cacheServer.setHostnameForClients(GEMFIRE_CACHE_SERVER_HOSTNAME_FOR_CLIENTS);
cacheServer.setPort(GEMFIRE_CACHE_SERVER_PORT);
cacheServer.start();
return gemfireCache;
}
private Cache registerShutdownHook(final Cache gemfireCache) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
if (gemfireCache != null) {
gemfireCache.close();
}
}
}));
return gemfireCache;
}
}

View File

@@ -1,49 +0,0 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Session Attributes</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" href="/webjars/bootstrap/css/bootstrap.min.css"/>
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>
This application demonstrates how to use a GemFire instance to back your session. Notice that there
is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested
session id is.
</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<tr th:each="attribute: ${sessionAttributes}">
<td th:text="${attribute.key}">name</td>
<td th:text="${attribute.value}">value</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,22 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-war'
apply plugin: "gemfire-server"
dependencies {
compile project(':spring-session-data-gemfire')
compile "org.springframework:spring-web"
compile "org.webjars:bootstrap"
compile "org.webjars:webjars-taglib"
compile jstlDependencies
compile slf4jDependencies
providedCompile "javax.servlet:javax.servlet-api"
runtime "org.springframework.shell:spring-shell"
testCompile "junit:junit"
testCompile "org.assertj:assertj-core"
integrationTestCompile seleniumDependencies
integrationTestRuntime "org.springframework.shell:spring-shell"
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2014-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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import sample.pages.HomePage;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class AttributeTests {
private WebDriver driver;
@Before
public void setup() {
this.driver = new HtmlUnitDriver();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void noAttributes() {
HomePage home = HomePage.go(this.driver, HomePage.class);
assertThat(home.attributes().size()).isEqualTo(0);
}
@Test
public void createAttribute() {
HomePage home = HomePage.go(this.driver, HomePage.class);
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
assertThat(home.attributes()).extracting("attributeName").containsOnly("a");
assertThat(home.attributes()).extracting("attributeValue").containsOnly("b");
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2014-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.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage {
private WebDriver driver;
@FindBy(tagName = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<Attribute>();
}
private static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
driver.get(baseUrl + get);
}
public static <T> T go(WebDriver driver, Class<T> page) {
get(driver, "/");
return PageFactory.initElements(driver, page);
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public HomePage logout() {
WebElement logout = this.driver
.findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(this.driver, HomePage.class);
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<Attribute>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,216 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.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.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";
@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>
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;
}
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<Pool> defaultPool = new AtomicReference<Pool>(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[]
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
// tag::class[]
public class Initializer extends AbstractHttpSessionApplicationInitializer { // <1>
public Initializer() {
super(ClientConfig.class); // <2>
}
}
// end::class[]

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
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.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
// tag::class[]
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <1>
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>
new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook();
}
@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>
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache 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(gemfireCache);
gemfireCacheServer.setHostNameForClients(SERVER_HOST);
gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
gemfireCacheServer.setPort(port);
return gemfireCacheServer;
}
}
// end::class[]

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// tag::class[]
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest 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[]

View File

@@ -1,50 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>This application demonstrates how to use a GemFire instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,19 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-war'
dependencies {
compile project(':spring-session-data-gemfire')
compile "org.springframework:spring-web"
compile "org.webjars:bootstrap"
compile "org.webjars:webjars-taglib"
compile jstlDependencies
compile slf4jDependencies
providedCompile "javax.servlet:javax.servlet-api"
testCompile "junit:junit"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.assertj:assertj-core"
integrationTestRuntime "org.springframework.shell:spring-shell"
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2014-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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import sample.pages.HomePage;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class AttributeTests {
private WebDriver driver;
@Before
public void setup() {
this.driver = new HtmlUnitDriver();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void noAttributes() {
HomePage home = HomePage.go(this.driver, HomePage.class);
assertThat(home.attributes().size()).isEqualTo(0);
}
@Test
public void createAttribute() {
HomePage home = HomePage.go(this.driver, HomePage.class);
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
assertThat(home.attributes()).extracting("attributeName").containsOnly("a");
assertThat(home.attributes()).extracting("attributeValue").containsOnly("b");
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2014-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.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage {
private WebDriver driver;
@FindBy(tagName = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<Attribute>();
}
private static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
driver.get(baseUrl + get);
}
public static <T> T go(WebDriver driver, Class<T> page) {
get(driver, "/");
return PageFactory.initElements(driver, page);
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public HomePage logout() {
WebElement logout = this.driver
.findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(this.driver, HomePage.class);
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<Attribute>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
// tag::class[]
@EnableGemFireHttpSession // <1>
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[]

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
// tag::class[]
public class Initializer extends AbstractHttpSessionApplicationInitializer { // <1>
public Initializer() {
super(Config.class); // <2>
}
}
// end::class[]

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// tag::class[]
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest 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[]

View File

@@ -1,50 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>This application demonstrates how to use a GemFire instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,22 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-war'
apply plugin: "gemfire-server"
dependencies {
compile project(':spring-session-data-gemfire')
compile "org.springframework:spring-web"
compile "org.webjars:bootstrap"
compile "org.webjars:webjars-taglib"
compile jstlDependencies
compile slf4jDependencies
providedCompile "javax.servlet:javax.servlet-api"
runtime "org.springframework.shell:spring-shell"
testCompile "junit:junit"
testCompile "org.assertj:assertj-core"
integrationTestCompile seleniumDependencies
integrationTestRuntime "org.springframework.shell:spring-shell"
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2014-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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import sample.pages.HomePage;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class AttributeTests {
private WebDriver driver;
@Before
public void setup() {
this.driver = new HtmlUnitDriver();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void noAttributes() {
HomePage home = HomePage.go(this.driver, HomePage.class);
assertThat(home.attributes().size()).isEqualTo(0);
}
@Test
public void createAttribute() {
HomePage home = HomePage.go(this.driver, HomePage.class);
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
assertThat(home.attributes()).extracting("attributeName").containsOnly("a");
assertThat(home.attributes()).extracting("attributeValue").containsOnly("b");
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2014-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.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage {
private WebDriver driver;
@FindBy(tagName = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<Attribute>();
}
private static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
driver.get(baseUrl + get);
}
public static <T> T go(WebDriver driver, Class<T> page) {
get(driver, "/");
return PageFactory.initElements(driver, page);
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public HomePage logout() {
WebElement logout = this.driver
.findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(this.driver, HomePage.class);
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<Attribute>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 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>
public class Application {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(Application.class).registerShutdownHook();
}
}
// tag::end[]

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.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.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 {
private static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(60);
private static final CountDownLatch LATCH = new CountDownLatch(1);
private static final String GEMFIRE_DEFAULT_POOL_NAME = "DEFAULT";
static {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
LATCH.countDown();
}
}
);
}
@Value("${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}")
private int port;
@Value("${application.gemfire.client-server.host:localhost}")
private String host;
private final AtomicBoolean checkGemFireServerIsRunning = new AtomicBoolean(true);
private final AtomicReference<Pool> gemfirePool = new AtomicReference<Pool>(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)
? 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);
}
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]", 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()) {
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;
}
}
// tag::end[]

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// tag::class[]
public class SessionServlet extends HttpServlet {
@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[]

View File

@@ -1,2 +0,0 @@
application.gemfire.client-server.host=localhost
application.gemfire.client-server.port=12480

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<!-- tag::beans[] -->
<!--1-->
<context:annotation-config/>
<!--2-->
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
<!--3-->
<util:properties id="gemfireProperties">
<prop key="name">GemFireClientServerHttpSessionXmlSample</prop>
<prop key="mcast-port">0</prop>
<!--<prop key="log-file">gemfire-server.log</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>
<!--4-->
<gfe:cache properties-ref="gemfireProperties"/>
<!--5-->
<gfe:cache-server auto-startup="true"
bind-address="${application.gemfire.client-server.host}"
host-name-for-clients="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
<!--6-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
<!-- end::beans[] -->
</beans>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<!-- tag::beans[] -->
<!--1-->
<context:annotation-config/>
<!--2-->
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
<!--3-->
<bean class="sample.GemFireClientServerReadyBeanPostProcessor"/>
<!--4-->
<util:properties id="gemfireProperties">
<!--<prop key="log-file">gemfire-client.log</prop>-->
<prop key="log-level">${spring.session.data.gemfire.log-level:warning}</prop>
</util:properties>
<!--5-->
<gfe:client-cache properties-ref="gemfireProperties" pool-name="gemfirePool"/>
<!--6-->
<gfe:pool keep-alive="false"
ping-interval="5000"
read-timeout="15000"
retry-attempts="1"
subscription-enabled="true"
thread-local-connections="false">
<gfe:server host="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
</gfe:pool>
<!--7-->
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30" p:poolName="DEFAULT"/>
<!-- end::beans[] -->
</beans>

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<!-- tag::context-param[] -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/session-client.xml</param-value>
</context-param>
<!-- end::context-param[] -->
<!-- tag::springSessionRepositoryFilter[] -->
<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>
<!-- end::springSessionRepositoryFilter[] -->
<!--
- Loads the root application context of this web app at startup.
- The application context is then available via
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<!-- tag::listeners[] -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- end::listeners[] -->
<servlet>
<servlet-name>session</servlet-name>
<servlet-class>sample.SessionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>session</servlet-name>
<url-pattern>/session</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@@ -1,50 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>This application demonstrates how to use a GemFire instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,19 +0,0 @@
apply plugin: 'io.spring.convention.spring-sample-war'
dependencies {
compile project(':spring-session-data-gemfire')
compile "org.springframework:spring-web"
compile "org.webjars:bootstrap"
compile "org.webjars:webjars-taglib"
compile jstlDependencies
compile slf4jDependencies
providedCompile "javax.servlet:javax.servlet-api"
testCompile "junit:junit"
integrationTestCompile seleniumDependencies
integrationTestCompile "org.assertj:assertj-core"
integrationTestRuntime "org.springframework.shell:spring-shell"
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2014-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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import sample.pages.HomePage;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
*/
public class AttributeTests {
private WebDriver driver;
@Before
public void setup() {
this.driver = new HtmlUnitDriver();
}
@After
public void tearDown() {
this.driver.quit();
}
@Test
public void noAttributes() {
HomePage home = HomePage.go(this.driver, HomePage.class);
assertThat(home.attributes().size()).isEqualTo(0);
}
@Test
public void createAttribute() {
HomePage home = HomePage.go(this.driver, HomePage.class);
home = home.form()
.attributeName("a")
.attributeValue("b")
.submit(HomePage.class);
assertThat(home.attributes()).extracting("attributeName").containsOnly("a");
assertThat(home.attributes()).extracting("attributeValue").containsOnly("b");
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2014-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.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eddú Meléndez
* @author Rob Winch
*/
public class HomePage {
private WebDriver driver;
@FindBy(tagName = "form")
WebElement form;
@FindBy(css = "table tbody tr")
List<WebElement> trs;
List<Attribute> attributes;
public HomePage(WebDriver driver) {
this.driver = driver;
this.attributes = new ArrayList<Attribute>();
}
private static void get(WebDriver driver, String get) {
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
driver.get(baseUrl + get);
}
public static <T> T go(WebDriver driver, Class<T> page) {
get(driver, "/");
return PageFactory.initElements(driver, page);
}
public void containCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").contains(cookieName);
}
public void doesNotContainCookie(String cookieName) {
Set<Cookie> cookies = this.driver.manage().getCookies();
assertThat(cookies).extracting("name").doesNotContain(cookieName);
}
public HomePage logout() {
WebElement logout = this.driver
.findElement(By.cssSelector("input[type=\"submit\"]"));
logout.click();
return PageFactory.initElements(this.driver, HomePage.class);
}
public List<Attribute> attributes() {
List<Attribute> rows = new ArrayList<Attribute>();
for (WebElement tr : this.trs) {
rows.add(new Attribute(tr));
}
this.attributes.addAll(rows);
return this.attributes;
}
public Form form() {
return new Form(this.form);
}
public class Form {
@FindBy(name = "attributeName")
WebElement attributeName;
@FindBy(name = "attributeValue")
WebElement attributeValue;
@FindBy(css = "input[type=\"submit\"]")
WebElement submit;
public Form(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
public Form attributeName(String text) {
this.attributeName.sendKeys(text);
return this;
}
public Form attributeValue(String text) {
this.attributeValue.sendKeys(text);
return this;
}
public <T> T submit(Class<T> page) {
this.submit.click();
return PageFactory.initElements(HomePage.this.driver, page);
}
}
public static class Attribute {
@FindBy(xpath = ".//td[1]")
WebElement attributeName;
@FindBy(xpath = ".//td[2]")
WebElement attributeValue;
public Attribute(SearchContext context) {
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
}
/**
* @return the attributeName
*/
public String getAttributeName() {
return this.attributeName.getText();
}
/**
* @return the attributeValue
*/
public String getAttributeValue() {
return this.attributeValue.getText();
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// tag::class[]
public class SessionServlet extends HttpServlet {
@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[]

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<!-- tag::beans[] -->
<!--1-->
<context:annotation-config/>
<context:property-placeholder/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"/>
<!--2-->
<util:properties id="gemfireProperties">
<prop key="name">GemFireP2PHttpSessionXmlSample</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
<prop key="jmx-manager">true</prop>
<prop key="jmx-manager-start">true</prop>
</util:properties>
<!--3-->
<gfe:cache properties-ref="gemfireProperties" use-bean-factory-locator="false"/>
<!-- end::beans[] -->
</beans>

View File

@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<!-- tag::context-param[] -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<!-- end::context-param[] -->
<!-- tag::springSessionRepositoryFilter[] -->
<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>
<!-- end::springSessionRepositoryFilter[] -->
<!--
- Loads the root application context of this web app at startup.
- The application context is then available via
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<!-- tag::listeners[] -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- end::listeners[] -->
<servlet>
<servlet-name>session</servlet-name>
<servlet-class>sample.SessionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>session</servlet-name>
<url-pattern>/session</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@@ -1,50 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session Attributes</title>
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
<style type="text/css">
body {
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<h1>Description</h1>
<p>This application demonstrates how to use a GemFire instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
<h1>Try it</h1>
<form class="form-inline" role="form" action="./session" method="post">
<label for="attributeName">Attribute Name</label>
<input id="attributeName" type="text" name="attributeName"/>
<label for="attributeValue">Attribute Value</label>
<input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/>
</form>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th>Attribute Name</th>
<th>Attribute Value</th>
</tr>
</thead>
<tbody>
<c:forEach items="${sessionScope}" var="attr">
<tr>
<td><c:out value="${attr.key}"/></td>
<td><c:out value="${attr.value}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,11 +0,0 @@
apply plugin: 'io.spring.convention.spring-pom'
description = "Aggregator for Spring Session and Spring Data GemFire"
dependencies {
compile project(':spring-session')
compile("org.springframework.data:spring-data-gemfire") {
exclude group: "org.slf4j", module: 'slf4j-api'
exclude group: "org.slf4j", module: 'jcl-over-slf4j'
}
}

View File

@@ -10,7 +10,6 @@ dependencies {
optional "org.springframework:spring-messaging"
optional "org.springframework:spring-web"
optional "org.springframework:spring-websocket"
optional "org.springframework.data:spring-data-gemfire"
optional "org.springframework.data:spring-data-mongodb"
optional "org.springframework.data:spring-data-redis"
optional "org.springframework.security:spring-security-core"
@@ -27,7 +26,6 @@ dependencies {
integrationTestCompile "org.hsqldb:hsqldb"
integrationTestCompile "de.flapdoodle.embed:de.flapdoodle.embed.mongo"
integrationTestRuntime "org.springframework.shell:spring-shell"
testCompile "junit:junit"
testCompile "org.mockito:mockito-core"

View File

@@ -1,460 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.server.CacheServer;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.AbstractSessionEvent;
import static org.assertj.core.api.Assertions.assertThat;
/**
* {@link AbstractGemFireIntegrationTests} is an abstract base class encapsulating common functionality
* for writing Spring Session GemFire integration tests.
*
* @author John Blum
* @since 1.1.0
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.server.CacheServer
* @see org.springframework.session.ExpiringSession
*/
public abstract class AbstractGemFireIntegrationTests {
protected static final boolean DEFAULT_ENABLE_QUERY_DEBUGGING = false;
protected static final boolean GEMFIRE_QUERY_DEBUG = Boolean.getBoolean("spring.session.data.gemfire.query.debug");
protected static final int DEFAULT_GEMFIRE_SERVER_PORT = CacheServer.DEFAULT_PORT;
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
protected static final long DEFAULT_WAIT_INTERVAL = 500L;
protected static final File WORKING_DIRECTORY = new File(System.getProperty("user.dir"));
protected static final String DEFAULT_PROCESS_CONTROL_FILENAME = "process.ctl";
protected static final String GEMFIRE_LOG_FILE_NAME =
System.getProperty("spring.session.data.gemfire.log-file", "server.log");
protected static final String GEMFIRE_LOG_LEVEL =
System.getProperty("spring.session.data.gemfire.log-level", "error");
@Autowired
protected Cache gemfireCache;
@Autowired
protected GemFireOperationsSessionRepository gemfireSessionRepository;
@Before
public void setup() {
System.setProperty("gemfire.Query.VERBOSE", String.valueOf(isQueryDebuggingEnabled()));
}
/* (non-Javadoc) */
protected static File createDirectory(String pathname) {
File directory = new File(WORKING_DIRECTORY, pathname);
assertThat(directory.isDirectory() || directory.mkdirs())
.as(String.format("Failed to create directory (%1$s)", directory)).isTrue();
directory.deleteOnExit();
return directory;
}
/* (non-Javadoc) */
protected static List<String> createJavaProcessCommandLine(Class<?> type, String... args) {
List<String> commandLine = new ArrayList<String>();
String javaHome = System.getProperty("java.home");
String javaExe = new File(new File(javaHome, "bin"), "java").getAbsolutePath();
commandLine.add(javaExe);
commandLine.add("-server");
commandLine.add("-ea");
commandLine.add(String.format("-Dgemfire.log-file=%1$s", GEMFIRE_LOG_FILE_NAME));
commandLine.add(String.format("-Dgemfire.log-level=%1$s", GEMFIRE_LOG_LEVEL));
commandLine.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
commandLine.addAll(extractJvmArguments(args));
commandLine.add("-classpath");
commandLine.add(System.getProperty("java.class.path"));
commandLine.add(type.getName());
commandLine.addAll(extractProgramArguments(args));
// System.err.printf("Java process command-line is (%1$s)%n", commandLine);
return commandLine;
}
/* (non-Javadoc) */
protected static List<String> extractJvmArguments(final String... args) {
List<String> jvmArgs = new ArrayList<String>(args.length);
for (String arg : args) {
if (arg.startsWith("-")) {
jvmArgs.add(arg);
}
}
return jvmArgs;
}
/* (non-Javadoc) */
protected static List<String> extractProgramArguments(final String... args) {
List<String> jvmArgs = new ArrayList<String>(args.length);
for (String arg : args) {
if (!arg.startsWith("-")) {
jvmArgs.add(arg);
}
}
return jvmArgs;
}
/* (non-Javadoc) */
protected static Process run(Class<?> type, File directory, String... args) throws IOException {
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args)).directory(directory).start();
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer) {
return waitForCacheServerToStart(cacheServer, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(String host, int port) {
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
public boolean evaluate() {
Socket socket = null;
try {
if (!connected.get()) {
socket = new Socket(host, port);
connected.set(true);
}
}
catch (IOException ignore) {
}
finally {
GemFireUtils.close(socket);
}
return connected.get();
}
}, duration);
}
// NOTE this method would not be necessary except Spring Sessions' build does not fork
// the test JVM
// for every test class.
/* (non-Javadoc) */
protected static boolean waitForClientCacheToClose() {
return waitForClientCacheToClose(DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static boolean waitForClientCacheToClose(long duration) {
try {
final ClientCache clientCache = ClientCacheFactory.getAnyInstance();
clientCache.close();
waitOnCondition(new Condition() {
public boolean evaluate() {
return clientCache.isClosed();
}
}, duration);
return clientCache.isClosed();
}
catch (CacheClosedException ignore) {
return true;
}
}
/* (non-Javadoc) */
protected static boolean waitForProcessToStart(Process process, File directory) {
return waitForProcessToStart(process, directory, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static boolean waitForProcessToStart(Process process, File directory, long duration) {
final File processControl = new File(directory, DEFAULT_PROCESS_CONTROL_FILENAME);
waitOnCondition(new Condition() {
public boolean evaluate() {
return processControl.isFile();
}
}, duration);
return process.isAlive();
}
/* (non-Javadoc) */
protected static int waitForProcessToStop(Process process, File directory) {
return waitForProcessToStop(process, directory, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static int waitForProcessToStop(Process process, File directory, long duration) {
long timeout = (System.currentTimeMillis() + duration);
try {
while (process.isAlive() && System.currentTimeMillis() < timeout) {
if (process.waitFor(DEFAULT_WAIT_INTERVAL, TimeUnit.MILLISECONDS)) {
return process.exitValue();
}
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return (process.isAlive() ? -1 : process.exitValue());
}
/* (non-Javadoc) */
protected static boolean waitOnCondition(Condition condition) {
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static boolean waitOnCondition(Condition condition, long duration) {
long timeout = (System.currentTimeMillis() + duration);
try {
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
synchronized (condition) {
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
}
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return condition.evaluate();
}
/* (non-Javadoc) */
protected static File writeProcessControlFile(File path) throws IOException {
assertThat(path != null && path.isDirectory()).isTrue();
File processControl = new File(path, DEFAULT_PROCESS_CONTROL_FILENAME);
assertThat(processControl.createNewFile()).isTrue();
processControl.deleteOnExit();
return processControl;
}
/* (non-Javadoc) */
protected void assertValidSession(ExpiringSession session) {
assertThat(session).isNotNull();
assertThat(session.getId()).isNotEmpty();
assertThat(session.isExpired()).isFalse();
}
/* (non-Javadoc) */
protected void assertRegion(Region<?, ?> actualRegion, String expectedName, DataPolicy expectedDataPolicy) {
assertThat(actualRegion).isNotNull();
assertThat(actualRegion.getName()).isEqualTo(expectedName);
assertThat(actualRegion.getFullPath()).isEqualTo(GemFireUtils.toRegionPath(expectedName));
assertThat(actualRegion.getAttributes()).isNotNull();
assertThat(actualRegion.getAttributes().getDataPolicy()).isEqualTo(expectedDataPolicy);
}
/* (non-Javadoc) */
protected void assertIndex(Index index, String expectedExpression, String expectedFromClause) {
assertThat(index).isNotNull();
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
}
/* (non-Javadoc) */
protected void assertEntryIdleTimeout(Region<?, ?> region, ExpirationAction expectedAction, int expectedTimeout) {
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(), expectedAction, expectedTimeout);
}
/* (non-Javadoc) */
protected void assertEntryIdleTimeout(ExpirationAttributes actualExpirationAttributes,
ExpirationAction expectedAction, int expectedTimeout) {
assertThat(actualExpirationAttributes).isNotNull();
assertThat(actualExpirationAttributes.getAction()).isEqualTo(expectedAction);
assertThat(actualExpirationAttributes.getTimeout()).isEqualTo(expectedTimeout);
}
/* (non-Javadoc) */
protected boolean enableQueryDebugging() {
return DEFAULT_ENABLE_QUERY_DEBUGGING;
}
/* (non-Javadoc) */
protected boolean isQueryDebuggingEnabled() {
return (GEMFIRE_QUERY_DEBUG || enableQueryDebugging());
}
/* (non-Javadoc) */
protected List<String> listRegions(GemFireCache gemfireCache) {
Set<Region<?, ?>> regions = gemfireCache.rootRegions();
List<String> regionList = new ArrayList<String>(regions.size());
for (Region<?, ?> region : regions) {
regionList.add(region.getFullPath());
}
return regionList;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected <T extends ExpiringSession> T createSession() {
T expiringSession = (T) this.gemfireSessionRepository.createSession();
assertThat(expiringSession).isNotNull();
return expiringSession;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected <T extends ExpiringSession> T createSession(String principalName) {
GemFireOperationsSessionRepository.GemFireSession session = createSession();
session.setPrincipalName(principalName);
return (T) session;
}
/* (non-Javadoc) */
protected <T extends ExpiringSession> T delete(T session) {
this.gemfireSessionRepository.delete(session);
return session;
}
/* (non-Javadoc) */
protected <T extends ExpiringSession> T expire(T session) {
session.setLastAccessedTime(0L);
return session;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected <T extends ExpiringSession> T get(String sessionId) {
return (T) this.gemfireSessionRepository.getSession(sessionId);
}
/* (non-Javadoc) */
protected <T extends ExpiringSession> T save(T session) {
this.gemfireSessionRepository.save(session);
return session;
}
/* (non-Javadoc) */
protected <T extends ExpiringSession> T touch(T session) {
session.setLastAccessedTime(System.currentTimeMillis());
return session;
}
/**
* The SessionEventListener class is a Spring {@link ApplicationListener} listening
* for Spring HTTP Session application events.
*
* @see org.springframework.context.ApplicationListener
* @see org.springframework.session.events.AbstractSessionEvent
*/
public static class SessionEventListener implements ApplicationListener<AbstractSessionEvent> {
private volatile AbstractSessionEvent sessionEvent;
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
public <T extends AbstractSessionEvent> T getSessionEvent() {
T sessionEvent = (T) this.sessionEvent;
this.sessionEvent = null;
return sessionEvent;
}
/* (non-Javadoc) */
public void onApplicationEvent(AbstractSessionEvent event) {
this.sessionEvent = event;
}
/* (non-Javadoc) */
public <T extends AbstractSessionEvent> T waitForSessionEvent(long duration) {
waitOnCondition(new Condition() {
public boolean evaluate() {
return (SessionEventListener.this.sessionEvent != null);
}
}, duration);
return getSessionEvent();
}
}
/**
* The Condition interface defines a logical condition that must be satisfied before
* it is safe to proceed.
*/
protected interface Condition {
boolean evaluate();
}
}

View File

@@ -1,374 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
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.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests to test the functionality of GemFire-backed Spring Sessions using
* the GemFire client-server topology.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.server.CacheServer
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes =
ClientServerGemFireOperationsSessionRepositoryIntegrationTests.SpringSessionDataGemFireClientConfiguration.class)
@DirtiesContext
@WebAppConfiguration
public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
private static Process gemfireServer;
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestClientServerSessions";
@Autowired
private SessionEventListener sessionEventListener;
@BeforeClass
public static void startGemFireServer() throws IOException {
long t0 = System.currentTimeMillis();
int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting a GemFire Server running on host [%1$s] listening on port [%2$d]%n",
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname =
String.format("gemfire-client-server-tests-%1$s", TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionDataGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port))
.isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
}
@AfterClass
public static void stopGemFireServer() {
if (gemfireServer != null) {
gemfireServer.destroy();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
}
@Before
public void setup() {
assertThat(GemFireUtils.isClient(gemfireCache)).isTrue();
Region<Object, ExpiringSession> springSessionGemFireRegion =
gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(springSessionGemFireRegion).isNotNull();
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes =
springSessionGemFireRegion.getAttributes();
assertThat(springSessionGemFireRegionAttributes).isNotNull();
assertThat(springSessionGemFireRegionAttributes.getDataPolicy()).isEqualTo(DataPolicy.NORMAL);
}
@After
public void tearDown() {
this.sessionEventListener.getSessionEvent();
}
@Test
public void createSessionFiresSessionCreatedEvent() {
long beforeOrAtCreationTime = System.currentTimeMillis();
ExpiringSession expectedSession = save(createSession());
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
ExpiringSession createdSession = sessionEvent.getSession();
assertThat(createdSession.getId()).isEqualTo(expectedSession.getId());
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
createdSession.setAttribute("attrOne", 1);
assertThat(save(touch(createdSession)).<Integer>getAttribute("attrOne")).isEqualTo(1);
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isNull();
this.gemfireSessionRepository.delete(expectedSession.getId());
}
@Test
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
assertThat(this.sessionEventListener.<SessionCreatedEvent>getSessionEvent()).isNull();
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
sessionEvent = this.sessionEventListener.waitForSessionEvent(
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession expiredSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(expiredSession).isNull();
}
@Test
public void deleteExistingNonExpiredSessionFiresSessionDeletedEventAndReturnsNullOnGet() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
this.gemfireSessionRepository.delete(expectedSession.getId());
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession deletedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(deletedSession).isNull();
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionDataGemFireClientConfiguration {
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
return clientCacheFactory;
}
@Bean
PoolFactoryBean gemfirePool(
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setKeepAlive(false);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(1);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setServers(Collections.singletonList(new ConnectionEndpoint(
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
public SessionEventListener sessionEventListener() {
return new SessionEventListener();
}
// used for debugging purposes
@SuppressWarnings("resource")
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(
SpringSessionDataGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
}
}
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionDataGemFireServerConfiguration {
static final String SERVER_HOSTNAME = "localhost";
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", name());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
String name() {
return ClientServerGemFireOperationsSessionRepositoryIntegrationTests.class.getName();
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setPort(port);
return cacheServerFactory;
}
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringSessionDataGemFireServerConfiguration.class);
context.registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}
}
}

View File

@@ -1,285 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.client.ClientCache;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
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.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests testing the addition/removal of HTTP Session Attributes
* and the proper persistence of the HTTP Session state in a GemFire cache
* across a client/server topology.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @since 1.3.1
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes =
ClientServerHttpSessionAttributesDeltaIntegrationTests.SpringSessionDataGemFireClientConfiguration.class)
public class ClientServerHttpSessionAttributesDeltaIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
private static Process gemfireServer;
@BeforeClass
public static void startGemFireServer() throws IOException {
long t0 = System.currentTimeMillis();
int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting a GemFire Server running on host [%1$s] listening on port [%2$d]%n",
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname =
String.format("gemfire-client-server-tests-%1$s", TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionDataGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port))
.isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
}
@AfterClass
public static void stopGemFireServer() {
if (gemfireServer != null) {
gemfireServer.destroy();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
}
@Test
public void sessionCreationAndAccessIsSuccessful() {
ExpiringSession session = save(touch(createSession()));
assertThat(session).isNotNull();
assertThat(session.isExpired()).isFalse();
session.setAttribute("attrOne", 1);
session.setAttribute("attrTwo", 2);
save(touch(session));
ExpiringSession loadedSession = get(session.getId());
assertThat(loadedSession).isNotNull();
assertThat(loadedSession.isExpired()).isFalse();
assertThat(loadedSession).isNotSameAs(session);
assertThat(loadedSession.getId()).isEqualTo(session.getId());
assertThat(loadedSession.<Integer>getAttribute("attrOne")).isEqualTo(1);
assertThat(loadedSession.<Integer>getAttribute("attrTwo")).isEqualTo(2);
loadedSession.removeAttribute("attrTwo");
assertThat(loadedSession.getAttributeNames()).doesNotContain("attrTwo");
assertThat(loadedSession.getAttributeNames()).hasSize(1);
save(touch(loadedSession));
ExpiringSession reloadedSession = get(loadedSession.getId());
assertThat(reloadedSession).isNotNull();
assertThat(reloadedSession.isExpired()).isFalse();
assertThat(reloadedSession).isNotSameAs(loadedSession);
assertThat(reloadedSession.getId()).isEqualTo(loadedSession.getId());
assertThat(reloadedSession.getAttributeNames()).hasSize(1);
assertThat(reloadedSession.getAttributeNames()).doesNotContain("attrTwo");
assertThat(reloadedSession.<Integer>getAttribute("attrOne")).isEqualTo(1);
}
@EnableGemFireHttpSession
static class SpringSessionDataGemFireClientConfiguration {
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
return clientCacheFactory;
}
@Bean
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setKeepAlive(false);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(1);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setServers(Collections.singletonList(new ConnectionEndpoint(
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port)));
return poolFactory;
}
// used for debugging purposes
@SuppressWarnings("resource")
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext =
new AnnotationConfigApplicationContext(SpringSessionDataGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
}
}
}
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionDataGemFireServerConfiguration {
static final String SERVER_HOSTNAME = "localhost";
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", name());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
String name() {
return ClientServerHttpSessionAttributesDeltaIntegrationTests.class.getName();
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setPort(port);
return cacheServerFactory;
}
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringSessionDataGemFireServerConfiguration.class);
context.registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}
}
}

View File

@@ -1,309 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.client.ClientCache;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
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.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The ClientServerProxyRegionSessionOperationsIntegrationTests class...
*
* @author John Blum
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes =
ClientServerProxyRegionSessionOperationsIntegrationTests.SpringSessionDataGemFireClientConfiguration.class)
public class ClientServerProxyRegionSessionOperationsIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
private static Process gemfireServer;
@Autowired
private SessionEventListener sessionEventListener;
@BeforeClass
public static void startGemFireServer() throws IOException {
long t0 = System.currentTimeMillis();
int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting a GemFire Server running on host [%1$s] listening on port [%2$d]%n",
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname =
String.format("gemfire-client-server-tests-%1$s", TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionDataGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port))
.isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
}
@AfterClass
public static void stopGemFireServer() {
if (gemfireServer != null) {
gemfireServer.destroy();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
}
@Test
public void createReadUpdateExpireRecreateDeleteRecreateSessionResultsCorrectSessionCreatedEvents() {
ExpiringSession session = save(touch(createSession()));
assertValidSession(session);
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(session.getId());
// GET
ExpiringSession loadedSession = get(session.getId());
assertThat(loadedSession).isNotNull();
assertThat(loadedSession.getId()).isEqualTo(session.getId());
assertThat(loadedSession.getCreationTime()).isEqualTo(session.getCreationTime());
assertThat(loadedSession.getLastAccessedTime()).isGreaterThanOrEqualTo((session.getLastAccessedTime()));
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isNull();
loadedSession.setAttribute("attrOne", 1);
loadedSession.setAttribute("attrTwo", 2);
// UPDATE
save(touch(loadedSession));
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isNull();
// EXPIRE
sessionEvent = this.sessionEventListener.waitForSessionEvent(
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(session.getId());
// RECREATE
save(touch(session));
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(session.getId());
// DELETE
delete(session);
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(session.getId());
// RECREATE
save(touch(session));
sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(session.getId());
}
@EnableGemFireHttpSession
static class SpringSessionDataGemFireClientConfiguration {
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
return clientCacheFactory;
}
@Bean
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setKeepAlive(false);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(1);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setServers(Collections.singletonList(new ConnectionEndpoint(
SpringSessionDataGemFireServerConfiguration.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
public SessionEventListener sessionEventListener() {
return new SessionEventListener();
}
// used for debugging purposes
@SuppressWarnings("resource")
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext =
new AnnotationConfigApplicationContext(SpringSessionDataGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
}
}
}
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionDataGemFireServerConfiguration {
static final String SERVER_HOSTNAME = "localhost";
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", name());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
String name() {
return ClientServerProxyRegionSessionOperationsIntegrationTests.class.getName();
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setPort(port);
return cacheServerFactory;
}
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringSessionDataGemFireServerConfiguration.class);
context.registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}
}
}

View File

@@ -1,459 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.SelectResults;
import org.apache.geode.pdx.PdxReader;
import org.apache.geode.pdx.PdxSerializable;
import org.apache.geode.pdx.PdxWriter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test to test the {@code findByPrincipalName} query method
* on {@link GemFireOperationsSessionRepository}.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 300;
private static final String GEMFIRE_LOG_LEVEL = "warning";
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestPartitionedSessions";
SecurityContext context;
SecurityContext changedContext;
@Before
public void setup() {
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext = SecurityContextHolder.createEmptyContext();
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
"changedContext-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
assertThat(this.gemfireCache).isNotNull();
assertThat(this.gemfireSessionRepository).isNotNull();
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME, DataPolicy.PARTITION);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
protected Map<String, ExpiringSession> doFindByIndexNameAndIndexValue(
String indexName, String indexValue) {
return this.gemfireSessionRepository.findByIndexNameAndIndexValue(indexName,
indexValue);
}
protected Map<String, ExpiringSession> doFindByPrincipalName(String principalName) {
return doFindByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principalName);
}
@SuppressWarnings({ "unchecked" })
protected Map<String, ExpiringSession> doFindByPrincipalName(String regionName,
String principalName) {
try {
Region<String, ExpiringSession> region = this.gemfireCache.getRegion(regionName);
assertThat(region).isNotNull();
QueryService queryService = region.getRegionService().getQueryService();
String queryString = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
region.getFullPath());
Query query = queryService.newQuery(queryString);
SelectResults<ExpiringSession> results =
(SelectResults<ExpiringSession>) query.execute(new Object[] { principalName });
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
for (ExpiringSession session : results.asList()) {
sessions.put(session.getId(), session);
}
return sessions;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean enableQueryDebugging() {
return true;
}
protected ExpiringSession setAttribute(ExpiringSession session, String attributeName,
Object attributeValue) {
session.setAttribute(attributeName, attributeValue);
return session;
}
@Test
public void findSessionsByIndexedSessionAttributeNameValues() {
ExpiringSession johnBlumSession = save(touch(setAttribute(
createSession("johnBlum"), "vip", "yes")));
ExpiringSession robWinchSession = save(touch(setAttribute(
createSession("robWinch"), "vip", "yes")));
ExpiringSession jonDoeSession = save(touch(setAttribute(
createSession("jonDoe"), "vip", "no")));
ExpiringSession pieDoeSession = save(touch(setAttribute(
createSession("pieDoe"), "viper", "true")));
ExpiringSession sourDoeSession = save(touch(createSession("sourDoe")));
assertThat(this.<ExpiringSession>get(johnBlumSession.getId())).isEqualTo(johnBlumSession);
assertThat(johnBlumSession.<String>getAttribute("vip")).isEqualTo("yes");
assertThat(this.<ExpiringSession>get(robWinchSession.getId())).isEqualTo(robWinchSession);
assertThat(robWinchSession.<String>getAttribute("vip")).isEqualTo("yes");
assertThat(this.<ExpiringSession>get(jonDoeSession.getId())).isEqualTo(jonDoeSession);
assertThat(jonDoeSession.<String>getAttribute("vip")).isEqualTo("no");
assertThat(this.<ExpiringSession>get(pieDoeSession.getId())).isEqualTo(pieDoeSession);
assertThat(pieDoeSession.getAttributeNames().contains("vip")).isFalse();
assertThat(this.<ExpiringSession>get(sourDoeSession.getId())).isEqualTo(sourDoeSession);
assertThat(sourDoeSession.getAttributeNames().contains("vip")).isFalse();
Map<String, ExpiringSession> vipSessions = doFindByIndexNameAndIndexValue("vip", "yes");
assertThat(vipSessions).isNotNull();
assertThat(vipSessions.size()).isEqualTo(2);
assertThat(vipSessions.get(johnBlumSession.getId())).isEqualTo(johnBlumSession);
assertThat(vipSessions.get(robWinchSession.getId())).isEqualTo(robWinchSession);
assertThat(vipSessions.containsKey(jonDoeSession.getId()));
assertThat(vipSessions.containsKey(pieDoeSession.getId()));
assertThat(vipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> nonVipSessions = doFindByIndexNameAndIndexValue(
"vip", "no");
assertThat(nonVipSessions).isNotNull();
assertThat(nonVipSessions.size()).isEqualTo(1);
assertThat(nonVipSessions.get(jonDoeSession.getId())).isEqualTo(jonDoeSession);
assertThat(nonVipSessions.containsKey(johnBlumSession.getId()));
assertThat(nonVipSessions.containsKey(robWinchSession.getId()));
assertThat(nonVipSessions.containsKey(pieDoeSession.getId()));
assertThat(nonVipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> noSessions = doFindByIndexNameAndIndexValue(
"nonExistingAttribute", "test");
assertThat(noSessions).isNotNull();
assertThat(noSessions.isEmpty()).isTrue();
}
@Test
public void findSessionsByPrincipalName() {
ExpiringSession sessionOne = save(touch(createSession("robWinch")));
ExpiringSession sessionTwo = save(touch(createSession("johnBlum")));
ExpiringSession sessionThree = save(touch(createSession("robWinch")));
ExpiringSession sessionFour = save(touch(createSession("johnBlum")));
ExpiringSession sessionFive = save(touch(createSession("robWinch")));
assertThat(this.<ExpiringSession>get(sessionOne.getId())).isEqualTo(sessionOne);
assertThat(this.<ExpiringSession>get(sessionTwo.getId())).isEqualTo(sessionTwo);
assertThat(this.<ExpiringSession>get(sessionThree.getId())).isEqualTo(sessionThree);
assertThat(this.<ExpiringSession>get(sessionFour.getId())).isEqualTo(sessionFour);
assertThat(this.<ExpiringSession>get(sessionFive.getId())).isEqualTo(sessionFive);
Map<String, ExpiringSession> johnBlumSessions = doFindByPrincipalName("johnBlum");
assertThat(johnBlumSessions).isNotNull();
assertThat(johnBlumSessions.size()).isEqualTo(2);
assertThat(johnBlumSessions.containsKey(sessionOne.getId())).isFalse();
assertThat(johnBlumSessions.containsKey(sessionThree.getId())).isFalse();
assertThat(johnBlumSessions.containsKey(sessionFive.getId())).isFalse();
assertThat(johnBlumSessions.get(sessionTwo.getId())).isEqualTo(sessionTwo);
assertThat(johnBlumSessions.get(sessionFour.getId())).isEqualTo(sessionFour);
Map<String, ExpiringSession> robWinchSessions = doFindByPrincipalName("robWinch");
assertThat(robWinchSessions).isNotNull();
assertThat(robWinchSessions.size()).isEqualTo(3);
assertThat(robWinchSessions.containsKey(sessionTwo.getId())).isFalse();
assertThat(robWinchSessions.containsKey(sessionFour.getId())).isFalse();
assertThat(robWinchSessions.get(sessionOne.getId())).isEqualTo(sessionOne);
assertThat(robWinchSessions.get(sessionThree.getId())).isEqualTo(sessionThree);
assertThat(robWinchSessions.get(sessionFive.getId())).isEqualTo(sessionFive);
}
@Test
public void findSessionsBySecurityPrincipalName() {
ExpiringSession toSave = this.gemfireSessionRepository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
save(toSave);
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findSessionsByChangedSecurityPrincipalName() {
ExpiringSession toSave = this.gemfireSessionRepository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
save(toSave);
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = doFindByPrincipalName(getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
}
@Test
public void findsNoSessionsByNonExistingPrincipal() {
Map<String, ExpiringSession> nonExistingPrincipalSessions =
doFindByPrincipalName("nonExistingPrincipalName");
assertThat(nonExistingPrincipalSessions).isNotNull();
assertThat(nonExistingPrincipalSessions.isEmpty()).isTrue();
}
@Test
public void findsNoSessionsAfterPrincipalIsRemoved() {
String username = "doesNotFindAfterPrincipalRemoved";
ExpiringSession session = save(touch(createSession(username)));
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, null);
save(session);
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName(username);
assertThat(nonExistingPrincipalSessions).isNotNull();
assertThat(nonExistingPrincipalSessions.isEmpty()).isTrue();
}
@Test
public void saveAndReadSessionWithAttributes() {
ExpiringSession expectedSession = this.gemfireSessionRepository.createSession();
assertThat(expectedSession).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
((AbstractGemFireOperationsSessionRepository.GemFireSession) expectedSession).setPrincipalName("jblum");
List<String> expectedAttributeNames = Arrays.asList("booleanAttribute", "numericAttribute", "stringAttribute",
"personAttribute");
Person jonDoe = new Person("Jon", "Doe");
expectedSession.setAttribute(expectedAttributeNames.get(0), true);
expectedSession.setAttribute(expectedAttributeNames.get(1), Math.PI);
expectedSession.setAttribute(expectedAttributeNames.get(2), "test");
expectedSession.setAttribute(expectedAttributeNames.get(3), jonDoe);
this.gemfireSessionRepository.save(touch(expectedSession));
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
assertThat(savedSession).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(((AbstractGemFireOperationsSessionRepository.GemFireSession) savedSession).getPrincipalName())
.isEqualTo("jblum");
assertThat(savedSession.getAttributeNames().containsAll(expectedAttributeNames))
.as(String.format("Expected (%1$s); but was (%2$s)", expectedAttributeNames, savedSession.getAttributeNames()))
.isTrue();
assertThat(savedSession.<Boolean>getAttribute(expectedAttributeNames.get(0))).isTrue();
assertThat(savedSession.<Double>getAttribute(expectedAttributeNames.get(1))).isEqualTo(Math.PI);
assertThat(savedSession.<String>getAttribute(expectedAttributeNames.get(2))).isEqualTo("test");
assertThat(savedSession.<Person>getAttribute(expectedAttributeNames.get(3))).isEqualTo(jonDoe);
}
private String getSecurityName() {
return this.context.getAuthentication().getName();
}
private String getChangedSecurityName() {
return this.changedContext.getAuthentication().getName();
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireConfiguration {
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", GemFireOperationsSessionRepositoryIntegrationTests.class.getName());
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
}
public static class Person implements Comparable<Person>, PdxSerializable {
private String firstName;
private String lastName;
public Person() {
}
public Person(String firstName, String lastName) {
this.firstName = validate(firstName);
this.lastName = validate(lastName);
}
private String validate(String value) {
Assert.hasText(value, String.format("The String value (%1$s) must be specified!", value));
return value;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getName() {
return String.format("%1$s %2$s", getFirstName(), getLastName());
}
public void toData(PdxWriter pdxWriter) {
pdxWriter.writeString("firstName", getFirstName());
pdxWriter.writeString("lastName", getLastName());
}
public void fromData(final PdxReader pdxReader) {
this.firstName = pdxReader.readString("firstName");
this.lastName = pdxReader.readString("lastName");
}
@SuppressWarnings("all")
public int compareTo(final Person person) {
int compareValue = getLastName().compareTo(person.getLastName());
return (compareValue != 0 ? compareValue
: getFirstName().compareTo(person.getFirstName()));
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person that = (Person) obj;
return ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName())
&& ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName());
return hashValue;
}
@Override
public String toString() {
return getName();
}
}
}

View File

@@ -1,344 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.client.ClientCache;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
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.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test to test the functionality of a GemFire cache client in a Spring Session application
* using a specifically named GemFire {@link org.apache.geode.cache.client.Pool} configured with
* the 'poolName' attribute in the Spring Session Data GemFire {@link EnableGemFireHttpSession} annotation.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.server.CacheServer
* @since 1.3.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MultiPoolClientServerGemFireOperationsSessionRepositoryIntegrationTests
.SpringSessionGemFireClientConfiguration.class)
@DirtiesContext
@WebAppConfiguration
public class MultiPoolClientServerGemFireOperationsSessionRepositoryIntegrationTests
extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
private static Process gemfireServer;
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestMultiPoolClientServerSessions";
@Autowired
private SessionEventListener sessionEventListener;
@BeforeClass
public static void startGemFireServer() throws IOException {
final long t0 = System.currentTimeMillis();
final int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting a GemFire Server on [%1$s] listening on port [%2$d]%n",
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname = String.format("gemfire-multipool-client-server-tests-%1$s",
TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
}
@AfterClass
public static void stopGemFireServerAndDeleteArtifacts() {
if (gemfireServer != null) {
gemfireServer.destroyForcibly();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
}
@Before
public void setup() {
assertThat(GemFireUtils.isClient(gemfireCache)).isTrue();
Region<Object, ExpiringSession> springSessionGemFireRegion =
gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(springSessionGemFireRegion).isNotNull();
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes =
springSessionGemFireRegion.getAttributes();
assertThat(springSessionGemFireRegionAttributes).isNotNull();
assertThat(springSessionGemFireRegionAttributes.getDataPolicy()).isEqualTo(DataPolicy.EMPTY);
}
protected static ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
}
@Test
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
assertThat(this.sessionEventListener.<SessionCreatedEvent>getSessionEvent()).isNull();
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
this.sessionEventListener.getSessionEvent();
sessionEvent = this.sessionEventListener.waitForSessionEvent(
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession expiredSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(expiredSession).isNull();
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, poolName = "serverPool",
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireClientConfiguration {
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", name());
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
String name() {
return SpringSessionGemFireClientConfiguration.class.getName();
}
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setPoolName("gemfirePool");
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
PoolFactoryBean gemfirePool() {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMinConnections(0);
poolFactory.setReadTimeout(500);
// deliberately set to a non-existing GemFire (Cache) Server
poolFactory.addServers(newConnectionEndpoint("localhost", 53135));
return poolFactory;
}
@Bean
PoolFactoryBean serverPool(@Value("${spring.session.data.gemfire.port:"
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMaxConnections(SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(1);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setThreadLocalConnections(false);
poolFactory.addServers(newConnectionEndpoint(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port));
return poolFactory;
}
@Bean
public AbstractGemFireIntegrationTests.SessionEventListener sessionEventListener() {
return new AbstractGemFireIntegrationTests.SessionEventListener();
}
// used for debugging purposes
@SuppressWarnings("resource")
public static void main(final String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(
SpringSessionGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
}
}
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireServerConfiguration {
static final int MAX_CONNECTIONS = 50;
static final String SERVER_HOSTNAME = "localhost";
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", name());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-file", "server.log");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
String name() {
return SpringSessionGemFireServerConfiguration.class.getName();
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
cacheServerFactory.setPort(port);
return cacheServerFactory;
}
@SuppressWarnings("resource")
public static void main(final String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SpringSessionGemFireServerConfiguration.class);
context.registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}
}
}

View File

@@ -1,254 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The EnableGemFireHttpSessionEventsIntegrationTests class is a test suite of test cases
* testing the Session Event functionality and behavior of the
* GemFireOperationsSessionRepository and GemFire's configuration.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.events.SessionCreatedEvent
* @see org.springframework.session.events.SessionDeletedEvent
* @see org.springframework.session.events.SessionExpiredEvent
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Region
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final String GEMFIRE_LOG_LEVEL = "warning";
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestReplicatedSessions";
@Autowired
private SessionEventListener sessionEventListener;
@Before
public void setup() {
assertThat(GemFireUtils.isPeer(this.gemfireCache)).isTrue();
assertThat(this.gemfireSessionRepository).isNotNull();
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.sessionEventListener).isNotNull();
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME, DataPolicy.REPLICATE);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@After
public void tearDown() {
this.sessionEventListener.getSessionEvent();
}
@Test
public void sessionCreatedEvent() {
final long beforeOrAtCreationTime = System.currentTimeMillis();
ExpiringSession expectedSession = save(createSession());
AbstractSessionEvent sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
ExpiringSession createdSession = sessionEvent.getSession();
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.getId()).isNotNull();
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(createdSession.isExpired()).isFalse();
}
@Test
public void getExistingNonExpiredSession() {
ExpiringSession expectedSession = save(touch(createSession()));
assertThat(expectedSession.isExpired()).isFalse();
// NOTE though unlikely, a possible race condition exists between save and get...
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
}
@Test
public void getExistingExpiredSession() {
ExpiringSession expectedSession = save(expire(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
ExpiringSession createdSession = sessionEvent.getSession();
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.isExpired()).isTrue();
assertThat(this.gemfireSessionRepository.getSession(createdSession.getId())).isNull();
}
@Test
public void getNonExistingSession() {
assertThat(this.gemfireSessionRepository.getSession(UUID.randomUUID().toString())).isNull();
}
@Test
public void deleteExistingNonExpiredSession() {
ExpiringSession expectedSession = save(touch(createSession()));
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
assertThat(savedSession.isExpired()).isFalse();
this.gemfireSessionRepository.delete(savedSession.getId());
AbstractSessionEvent sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(savedSession.getId());
ExpiringSession deletedSession = sessionEvent.getSession();
assertThat(deletedSession).isEqualTo(savedSession);
assertThat(this.gemfireSessionRepository.getSession(deletedSession.getId())).isNull();
}
@Test
public void deleteExistingExpiredSession() {
ExpiringSession expectedSession = save(createSession());
AbstractSessionEvent sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
ExpiringSession createdSession = sessionEvent.getSession();
assertThat(createdSession).isEqualTo(expectedSession);
sessionEvent = this.sessionEventListener.waitForSessionEvent(
TimeUnit.SECONDS.toMillis(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds() + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
ExpiringSession expiredSession = sessionEvent.getSession();
assertThat(expiredSession).isEqualTo(createdSession);
assertThat(expiredSession.isExpired()).isTrue();
this.gemfireSessionRepository.delete(expectedSession.getId());
sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isNull();
assertThat(sessionEvent.getSessionId()).isEqualTo(expiredSession.getId());
assertThat(this.gemfireSessionRepository.<ExpiringSession>getSession(sessionEvent.getSessionId())).isNull();
}
@Test
public void deleteNonExistingSession() {
String expectedSessionId = UUID.randomUUID().toString();
assertThat(this.gemfireSessionRepository.<ExpiringSession>getSession(expectedSessionId)).isNull();
this.gemfireSessionRepository.delete(expectedSessionId);
AbstractSessionEvent sessionEvent = this.sessionEventListener.getSessionEvent();
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isNull();
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS, serverRegionShortcut = RegionShortcut.REPLICATE)
static class SpringSessionGemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name",
EnableGemFireHttpSessionEventsIntegrationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
SessionEventListener sessionEventListener() {
return new SessionEventListener();
}
}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.QueryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test to test the configuration of Spring Session backed by GemFire
* using Java-based configuration meta-data.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireHttpSessionJavaConfigurationTests extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache,
String regionName, DataPolicy dataPolicy) {
assertThat(GemFireUtils.isPeer(gemfireCache)).isTrue();
Region<K, V> region = gemfireCache.getRegion(regionName);
assertRegion(region, regionName, dataPolicy);
return region;
}
@Test
public void gemfireCacheConfigurationIsValid() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "JavaExample", DataPolicy.REPLICATE);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 900);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "JavaExample", DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index principalNameIndex = queryService.getIndex(example, "principalNameIndex");
assertIndex(principalNameIndex, "principalName", example.getFullPath());
}
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasNotCreated() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "JavaExample", DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
assertThat(sessionAttributesIndex).isNull();
}
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900,
regionName = "JavaExample", serverRegionShortcut = RegionShortcut.REPLICATE)
public static class GemFireConfiguration {
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", logLevel());
return gemfireProperties;
}
String applicationName() {
return GemFireHttpSessionJavaConfigurationTests.class.getName();
}
String logLevel() {
return "warning";
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean cacheFactory = new CacheFactoryBean();
cacheFactory.setClose(true);
cacheFactory.setProperties(gemfireProperties());
return cacheFactory;
}
}
}

View File

@@ -1,113 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.QueryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test suite of test cases testing the configuration of Spring Session backed by GemFire
* using XML configuration meta-data.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireHttpSessionXmlConfigurationTests extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache,
String regionName, DataPolicy dataPolicy) {
assertThat(GemFireUtils.isPeer(gemfireCache)).isTrue();
Region<K, V> region = gemfireCache.getRegion(regionName);
assertRegion(region, regionName, dataPolicy);
return region;
}
@Test
public void gemfireCacheConfigurationIsValid() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 3600);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index principalNameIndex = queryService.getIndex(example, "principalNameIndex");
assertIndex(principalNameIndex, "principalName", example.getFullPath());
}
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example =
assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
assertIndex(sessionAttributesIndex, "s.attributes['one', 'two', 'three']",
String.format("%1$s s", example.getFullPath()));
}
}

View File

@@ -1,956 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geode.DataSerializable;
import org.apache.geode.DataSerializer;
import org.apache.geode.Delta;
import org.apache.geode.Instantiator;
import org.apache.geode.InvalidDeltaException;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.Operation;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.util.CacheListenerAdapter;
import org.apache.geode.internal.concurrent.ConcurrentHashSet;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.gemfire.GemfireAccessor;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@link AbstractGemFireOperationsSessionRepository} is an abstract base class encapsulating functionality
* common to all implementations that support {@link SessionRepository} operations backed by GemFire.
*
* @author John Blum
* @since 1.1.0
* @see org.apache.geode.DataSerializable
* @see org.apache.geode.DataSerializer
* @see org.apache.geode.Delta
* @see org.apache.geode.Instantiator
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.util.CacheListenerAdapter
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.context.ApplicationEventPublisher
* @see org.springframework.context.ApplicationEventPublisherAware
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.Session
* @see org.springframework.session.SessionRepository
* @see org.springframework.session.FindByIndexNameSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
*/
public abstract class AbstractGemFireOperationsSessionRepository extends CacheListenerAdapter<Object, ExpiringSession>
implements ApplicationEventPublisherAware, FindByIndexNameSessionRepository<ExpiringSession>, InitializingBean {
private int maxInactiveIntervalInSeconds = GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
private ApplicationEventPublisher applicationEventPublisher = new ApplicationEventPublisher() {
public void publishEvent(ApplicationEvent event) {
}
public void publishEvent(Object event) {
}
};
private final Set<Integer> cachedSessionIds = new ConcurrentHashSet<Integer>();
private final GemfireOperations template;
protected final Log logger = newLogger();
private String fullyQualifiedRegionName;
/**
* Constructs an instance of AbstractGemFireOperationsSessionRepository with a
* required GemfireOperations instance used to perform GemFire data access operations
* and interactions supporting the SessionRepository operations.
*
* @param template the GemfireOperations instance used to interact with GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
*/
public AbstractGemFireOperationsSessionRepository(GemfireOperations template) {
Assert.notNull(template, "GemfireOperations must not be null");
this.template = template;
}
/**
* Used for testing purposes only to override the Log implementation with a mock.
*
* @return an instance of Log constructed from Apache commons-logging LogFactory.
* @see org.apache.commons.logging.LogFactory#getLog(Class)
*/
Log newLogger() {
return LogFactory.getLog(getClass());
}
/**
* Sets the ApplicationEventPublisher used to publish Session events corresponding to
* GemFire cache events.
*
* @param applicationEventPublisher the Spring ApplicationEventPublisher used to
* publish Session-based events.
* @see org.springframework.context.ApplicationEventPublisher
*/
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher, "ApplicationEventPublisher must not be null");
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* Gets the ApplicationEventPublisher used to publish Session events corresponding to
* GemFire cache events.
*
* @return the Spring ApplicationEventPublisher used to publish Session-based events.
* @see org.springframework.context.ApplicationEventPublisher
*/
protected ApplicationEventPublisher getApplicationEventPublisher() {
return this.applicationEventPublisher;
}
/**
* Gets the fully-qualified name of the GemFire cache {@link Region} used to store and
* manage Session data.
*
* @return a String indicating the fully qualified name of the GemFire cache
* {@link Region} used to store and manage Session data.
*/
protected String getFullyQualifiedRegionName() {
return this.fullyQualifiedRegionName;
}
/**
* Sets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum
* interval in seconds that a Session can remain inactive before it is considered
* expired.
*/
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
/**
* Gets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @return an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
*/
public int getMaxInactiveIntervalInSeconds() {
return this.maxInactiveIntervalInSeconds;
}
/**
* Gets a reference to the GemfireOperations (template) used to perform data access
* operations and other interactions on the GemFire cache {@link Region} backing this
* SessionRepository.
*
* @return a reference to the GemfireOperations used to interact with GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
*/
public GemfireOperations getTemplate() {
return this.template;
}
/**
* Callback method during Spring bean initialization that will capture the fully-qualified name
* of the GemFire cache {@link Region} used to manage Session state and register this SessionRepository
* as a GemFire {@link org.apache.geode.cache.CacheListener}.
*
* Additionally, this method registers GemFire {@link Instantiator}s for the {@link GemFireSession}
* and {@link GemFireSessionAttributes} types to optimize GemFire's instantiation logic on deserialization
* using the data serialization framework when accessing the {@link Session}'s state stored in GemFire.
*
* @throws Exception if an error occurs during the initialization process.
*/
public void afterPropertiesSet() throws Exception {
GemfireOperations template = getTemplate();
Assert.isInstanceOf(GemfireAccessor.class, template);
Region<Object, ExpiringSession> region = ((GemfireAccessor) template).getRegion();
this.fullyQualifiedRegionName = region.getFullPath();
region.getAttributesMutator().addCacheListener(this);
Instantiator.register(GemFireSessionInstantiator.create());
Instantiator.register(GemFireSessionAttributesInstantiator.create());
}
/* (non-Javadoc) */
boolean isCreate(EntryEvent<?, ?> event) {
return (isCreate(event.getOperation()) && isNotUpdate(event) && isExpiringSessionOrNull(event.getNewValue()));
}
/* (non-Javadoc) */
private boolean isCreate(Operation operation) {
return (operation.isCreate() && !Operation.LOCAL_LOAD_CREATE.equals(operation));
}
/**
* Used to determine whether the developer is storing (HTTP) Sessions with other, arbitrary application
* domain objects in the same GemFire cache {@link Region}; crazier things have happened!
*
* @param obj {@link Object} to evaluate.
* @return a boolean value indicating whether the {@link Object} from the entry event is indeed
* a {@link ExpiringSession}.
* @see org.springframework.session.ExpiringSession
*/
private boolean isExpiringSessionOrNull(Object obj) {
return (obj instanceof ExpiringSession || obj == null);
}
/* (non-Javadoc) */
private boolean isNotUpdate(EntryEvent event) {
return (isNotProxyRegion() || !this.cachedSessionIds.contains(ObjectUtils.nullSafeHashCode(event.getKey())));
}
/* (non-Javadoc) */
private boolean isNotProxyRegion() {
return !isProxyRegion();
}
/* (non-Javadoc) */
private boolean isProxyRegion() {
return GemFireUtils.isProxy(((GemfireAccessor) getTemplate()).getRegion());
}
boolean forget(Object sessionId) {
return this.cachedSessionIds.remove(ObjectUtils.nullSafeHashCode(sessionId));
}
boolean remember(Object sessionId) {
return (isProxyRegion() && this.cachedSessionIds.add(ObjectUtils.nullSafeHashCode(sessionId)));
}
/* (non-Javadoc) */
ExpiringSession toExpiringSession(Object obj) {
return (obj instanceof ExpiringSession ? (ExpiringSession) obj : null);
}
/**
* Callback method triggered when an entry is created in the GemFire cache {@link Region}.
*
* @param event {@link EntryEvent} containing the details of the cache {@link Region} operation.
* @see org.apache.geode.cache.EntryEvent
* @see #handleCreated(String, ExpiringSession)
*/
@Override
public void afterCreate(EntryEvent<Object, ExpiringSession> event) {
if (isCreate(event)) {
handleCreated(event.getKey().toString(), toExpiringSession(event.getNewValue()));
}
}
/**
* Callback method triggered when an entry is destroyed in the GemFire cache
* {@link Region}.
*
* @param event an EntryEvent containing the details of the cache operation.
* @see org.apache.geode.cache.EntryEvent
* @see #handleDestroyed(String, ExpiringSession)
*/
@Override
public void afterDestroy(EntryEvent<Object, ExpiringSession> event) {
handleDestroyed(event.getKey().toString(), toExpiringSession(event.getOldValue()));
}
/**
* Callback method triggered when an entry is invalidated in the GemFire cache
* {@link Region}.
*
* @param event an EntryEvent containing the details of the cache operation.
* @see org.apache.geode.cache.EntryEvent
* @see #handleExpired(String, ExpiringSession)
*/
@Override
public void afterInvalidate(EntryEvent<Object, ExpiringSession> event) {
handleExpired(event.getKey().toString(), toExpiringSession(event.getOldValue()));
}
/**
* Deletes the given {@link Session} from GemFire.
*
* @param session {@link Session} to delete.
* @return {@literal null}.
* @see org.springframework.session.Session
* @see #delete(String)
*/
protected ExpiringSession delete(Session session) {
delete(session.getId());
return null;
}
/**
* Causes Session created events to be published to the Spring application context.
*
* @param sessionId a String indicating the ID of the Session.
* @param session a reference to the Session triggering the event.
* @see org.springframework.session.events.SessionCreatedEvent
* @see org.springframework.session.ExpiringSession
* @see #publishEvent(ApplicationEvent)
*/
protected void handleCreated(String sessionId, ExpiringSession session) {
remember(sessionId);
publishEvent(session != null ? new SessionCreatedEvent(this, session)
: new SessionCreatedEvent(this, sessionId));
}
/**
* Causes Session deleted events to be published to the Spring application context.
*
* @param sessionId a String indicating the ID of the Session.
* @param session a reference to the Session triggering the event.
* @see org.springframework.session.events.SessionDeletedEvent
* @see org.springframework.session.ExpiringSession
* @see #publishEvent(ApplicationEvent)
*/
protected void handleDeleted(String sessionId, ExpiringSession session) {
forget(sessionId);
publishEvent(session != null ? new SessionDeletedEvent(this, session)
: new SessionDeletedEvent(this, sessionId));
}
/**
* Causes Session destroyed events to be published to the Spring application context.
*
* @param sessionId a String indicating the ID of the Session.
* @param session a reference to the Session triggering the event.
* @see org.springframework.session.events.SessionDestroyedEvent
* @see org.springframework.session.ExpiringSession
* @see #publishEvent(ApplicationEvent)
*/
protected void handleDestroyed(String sessionId, ExpiringSession session) {
forget(sessionId);
publishEvent(session != null ? new SessionDestroyedEvent(this, session)
: new SessionDestroyedEvent(this, sessionId));
}
/**
* Causes Session expired events to be published to the Spring application context.
*
* @param sessionId a String indicating the ID of the Session.
* @param session a reference to the Session triggering the event.
* @see org.springframework.session.events.SessionExpiredEvent
* @see org.springframework.session.ExpiringSession
* @see #publishEvent(ApplicationEvent)
*/
protected void handleExpired(String sessionId, ExpiringSession session) {
forget(sessionId);
publishEvent(session != null ? new SessionExpiredEvent(this, session)
: new SessionExpiredEvent(this, sessionId));
}
/**
* Publishes the specified ApplicationEvent to the Spring application context.
*
* @param event the ApplicationEvent to publish.
* @see org.springframework.context.ApplicationEventPublisher#publishEvent(ApplicationEvent)
* @see org.springframework.context.ApplicationEvent
*/
protected void publishEvent(ApplicationEvent event) {
try {
getApplicationEventPublisher().publishEvent(event);
}
catch (Throwable t) {
this.logger.error(String.format("Error occurred publishing event [%s]", event), t);
}
}
/**
* Updates the {@link ExpiringSession#setLastAccessedTime(long)} property of the {@link ExpiringSession}.
*
* @param <T> {@link Class} sub-type of the {@link ExpiringSession}.
* @param expiringSession {@link ExpiringSession} to touch.
* @return the {@link ExpiringSession}.
* @see org.springframework.session.ExpiringSession#setLastAccessedTime(long)
*/
protected <T extends ExpiringSession> T touch(T expiringSession) {
expiringSession.setLastAccessedTime(System.currentTimeMillis());
return expiringSession;
}
/**
* GemFireSession is a GemFire representation model of a Spring {@link ExpiringSession}
* that stores and manages Session state information in GemFire. This class implements
* GemFire's {@link DataSerializable} interface to better handle replication of Session
* state information across the GemFire cluster.
*/
@SuppressWarnings("serial")
public static class GemFireSession implements Comparable<ExpiringSession>,
DataSerializable, Delta, ExpiringSession {
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
protected static final DateFormat TO_STRING_DATE_FORMAT = new SimpleDateFormat("YYYY-MM-dd-HH-mm-ss");
protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private transient boolean delta = false;
private int maxInactiveIntervalInSeconds;
private long creationTime;
private long lastAccessedTime;
private transient final GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(this);
private transient final SpelExpressionParser parser = new SpelExpressionParser();
private String id;
/* (non-Javadoc) */
protected GemFireSession() {
this(UUID.randomUUID().toString());
}
/* (non-Javadoc) */
protected GemFireSession(String id) {
this.id = validateId(id);
this.creationTime = System.currentTimeMillis();
this.lastAccessedTime = this.creationTime;
}
/* (non-Javadoc) */
protected GemFireSession(ExpiringSession session) {
Assert.notNull(session, "The ExpiringSession to copy cannot be null");
this.id = session.getId();
this.creationTime = session.getCreationTime();
this.lastAccessedTime = session.getLastAccessedTime();
this.maxInactiveIntervalInSeconds = session.getMaxInactiveIntervalInSeconds();
this.sessionAttributes.from(session);
}
/* (non-Javadoc) */
public static GemFireSession create(int maxInactiveIntervalInSeconds) {
GemFireSession session = new GemFireSession();
session.setMaxInactiveIntervalInSeconds(maxInactiveIntervalInSeconds);
return session;
}
public static GemFireSession copy(ExpiringSession session) {
return new GemFireSession(session);
}
/* (non-Javadoc) */
public static GemFireSession from(ExpiringSession session) {
return (session instanceof GemFireSession ? (GemFireSession) session : copy(session));
}
/* (non-Javadoc) */
private String validateId(String id) {
Assert.hasText(id, "ID must be specified");
return id;
}
/* (non-Javadoc) */
protected boolean allowJavaSerialization() {
return DEFAULT_ALLOW_JAVA_SERIALIZATION;
}
/* (non-Javadoc) */
public synchronized String getId() {
return this.id;
}
/* (non-Javadoc) */
public synchronized long getCreationTime() {
return this.creationTime;
}
/* (non-Javadoc) */
public void setAttribute(String attributeName, Object attributeValue) {
this.sessionAttributes.setAttribute(attributeName, attributeValue);
}
/* (non-Javadoc) */
public void removeAttribute(String attributeName) {
this.sessionAttributes.removeAttribute(attributeName);
}
/* (non-Javadoc) */
public <T> T getAttribute(String attributeName) {
return this.sessionAttributes.getAttribute(attributeName);
}
/* (non-Javadoc) */
public Set<String> getAttributeNames() {
return this.sessionAttributes.getAttributeNames();
}
/* (non-Javadoc) */
public GemFireSessionAttributes getAttributes() {
return this.sessionAttributes;
}
/* (non-Javadoc) */
public synchronized boolean isExpired() {
long lastAccessedTime = getLastAccessedTime();
long maxInactiveIntervalInSeconds = getMaxInactiveIntervalInSeconds();
return (maxInactiveIntervalInSeconds >= 0
&& (idleTimeout(maxInactiveIntervalInSeconds) >= lastAccessedTime));
}
/* (non-Javadoc) */
private long idleTimeout(long maxInactiveIntervalInSeconds) {
return (System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(maxInactiveIntervalInSeconds));
}
/* (non-Javadoc) */
public synchronized void setLastAccessedTime(long lastAccessedTime) {
this.delta |= (this.lastAccessedTime != lastAccessedTime);
this.lastAccessedTime = lastAccessedTime;
}
/* (non-Javadoc) */
public synchronized long getLastAccessedTime() {
return this.lastAccessedTime;
}
/* (non-Javadoc) */
public synchronized void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.delta |= (this.maxInactiveIntervalInSeconds != maxInactiveIntervalInSeconds);
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
/* (non-Javadoc) */
public synchronized int getMaxInactiveIntervalInSeconds() {
return this.maxInactiveIntervalInSeconds;
}
/* (non-Javadoc) */
public synchronized void setPrincipalName(String principalName) {
setAttribute(PRINCIPAL_NAME_INDEX_NAME, principalName);
}
/* (non-Javadoc) */
public synchronized String getPrincipalName() {
String principalName = getAttribute(PRINCIPAL_NAME_INDEX_NAME);
if (principalName == null) {
Object authentication = getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = this.parser.parseExpression("authentication?.name");
principalName = expression.getValue(authentication, String.class);
}
}
return principalName;
}
/* (non-Javadoc) */
public synchronized void toData(DataOutput out) throws IOException {
out.writeUTF(getId());
out.writeLong(getCreationTime());
out.writeLong(getLastAccessedTime());
out.writeInt(getMaxInactiveIntervalInSeconds());
String principalName = getPrincipalName();
int length = (StringUtils.hasText(principalName) ? principalName.length() : 0);
out.writeInt(length);
if (length > 0) {
out.writeUTF(principalName);
}
writeObject(this.sessionAttributes, out);
this.delta = false;
}
/* (non-Javadoc) */
void writeObject(Object obj, DataOutput out) throws IOException {
DataSerializer.writeObject(obj, out, allowJavaSerialization());
}
/* (non-Javadoc) */
public synchronized void fromData(DataInput in) throws ClassNotFoundException, IOException {
this.id = in.readUTF();
this.creationTime = in.readLong();
setLastAccessedTime(in.readLong());
setMaxInactiveIntervalInSeconds(in.readInt());
int principalNameLength = in.readInt();
if (principalNameLength > 0) {
setPrincipalName(in.readUTF());
}
this.sessionAttributes.from(this.<GemFireSessionAttributes>readObject(in));
this.delta = false;
}
/* (non-Javadoc) */
<T> T readObject(DataInput in) throws ClassNotFoundException, IOException {
return DataSerializer.readObject(in);
}
/* (non-Javadoc) */
public synchronized boolean hasDelta() {
return (this.delta || this.sessionAttributes.hasDelta());
}
/* (non-Javadoc) */
public synchronized void toDelta(DataOutput out) throws IOException {
out.writeLong(getLastAccessedTime());
out.writeInt(getMaxInactiveIntervalInSeconds());
this.sessionAttributes.toDelta(out);
this.delta = false;
}
/* (non-Javadoc) */
public synchronized void fromDelta(DataInput in) throws IOException {
setLastAccessedTime(in.readLong());
setMaxInactiveIntervalInSeconds(in.readInt());
this.sessionAttributes.fromDelta(in);
this.delta = false;
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public int compareTo(ExpiringSession session) {
return (Long.valueOf(getCreationTime()).compareTo(session.getCreationTime()));
}
/* (non-Javadoc) */
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Session)) {
return false;
}
Session that = (Session) obj;
return this.getId().equals(that.getId());
}
/* (non-Javadoc) */
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + getId().hashCode();
return hashValue;
}
/* (non-Javadoc) */
@Override
public synchronized String toString() {
return String.format("{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s"
+ ", maxInactiveIntervalInSeconds = %5$s, principalName = %6$s }",
getClass().getName(), getId(), toString(getCreationTime()), toString(getLastAccessedTime()),
getMaxInactiveIntervalInSeconds(), getPrincipalName());
}
/* (non-Javadoc) */
private String toString(long timestamp) {
return TO_STRING_DATE_FORMAT.format(new Date(timestamp));
}
}
/**
* GemFireSessionInstantiator is a GemFire {@link Instantiator} use to instantiate instances
* of the {@link GemFireSession} object used in GemFire's data serialization framework when
* persisting Session state in GemFire.
*/
public static class GemFireSessionInstantiator extends Instantiator {
public static GemFireSessionInstantiator create() {
return new GemFireSessionInstantiator(GemFireSession.class, 800813552);
}
public GemFireSessionInstantiator(Class<? extends DataSerializable> type, int id) {
super(type, id);
}
@Override
public DataSerializable newInstance() {
return new GemFireSession();
}
}
/**
* The GemFireSessionAttributes class is a container for Session attributes implementing
* both the {@link DataSerializable} and {@link Delta} GemFire interfaces for efficient
* storage and distribution (replication) in GemFire. Additionally, GemFireSessionAttributes
* extends {@link AbstractMap} providing {@link Map}-like behavior since attributes of a Session
* are effectively a name to value mapping.
*
* @see java.util.AbstractMap
* @see org.apache.geode.DataSerializable
* @see org.apache.geode.DataSerializer
* @see org.apache.geode.Delta
*/
@SuppressWarnings("serial")
public static class GemFireSessionAttributes extends AbstractMap<String, Object>
implements DataSerializable, Delta {
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
private transient final Map<String, Object> sessionAttributes = new HashMap<String, Object>();
private transient final Map<String, Object> sessionAttributeDeltas = new HashMap<String, Object>();
private transient final Object lock;
/* (non-Javadoc) */
protected GemFireSessionAttributes() {
this.lock = this;
}
/* (non-Javadoc) */
protected GemFireSessionAttributes(Object lock) {
this.lock = (lock != null ? lock : this);
}
/* (non-Javadoc) */
public void setAttribute(String attributeName, Object attributeValue) {
synchronized (this.lock) {
if (attributeValue != null) {
if (!attributeValue.equals(this.sessionAttributes.put(attributeName, attributeValue))) {
this.sessionAttributeDeltas.put(attributeName, attributeValue);
}
}
else {
removeAttribute(attributeName);
}
}
}
/* (non-Javadoc) */
public void removeAttribute(String attributeName) {
synchronized (this.lock) {
if (this.sessionAttributes.remove(attributeName) != null) {
this.sessionAttributeDeltas.put(attributeName, null);
}
}
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
public <T> T getAttribute(String attributeName) {
synchronized (this.lock) {
return (T) this.sessionAttributes.get(attributeName);
}
}
/* (non-Javadoc) */
public Set<String> getAttributeNames() {
synchronized (this.lock) {
return Collections.unmodifiableSet(new HashSet<String>(this.sessionAttributes.keySet()));
}
}
/* (non-Javadoc) */
protected boolean allowJavaSerialization() {
return DEFAULT_ALLOW_JAVA_SERIALIZATION;
}
/* (non-Javadoc); NOTE: entrySet implementation is not Thread-safe. */
@Override
@SuppressWarnings("all")
public Set<Entry<String, Object>> entrySet() {
return new AbstractSet<Entry<String, Object>>() {
@Override
public Iterator<Entry<String, Object>> iterator() {
return Collections.unmodifiableMap(GemFireSessionAttributes.this.sessionAttributes)
.entrySet().iterator();
}
@Override
public int size() {
return GemFireSessionAttributes.this.sessionAttributes.size();
}
};
}
/* (non-Javadoc) */
public void from(Session session) {
synchronized (this.lock) {
for (String attributeName : session.getAttributeNames()) {
setAttribute(attributeName, session.getAttribute(attributeName));
}
}
}
/* (non-Javadoc) */
public void from(GemFireSessionAttributes sessionAttributes) {
synchronized (this.lock) {
for (String attributeName : sessionAttributes.getAttributeNames()) {
setAttribute(attributeName, sessionAttributes.getAttribute(attributeName));
}
}
}
/* (non-Javadoc) */
public void toData(DataOutput out) throws IOException {
synchronized (this.lock) {
Set<String> attributeNames = getAttributeNames();
out.writeInt(attributeNames.size());
for (String attributeName : attributeNames) {
out.writeUTF(attributeName);
writeObject(getAttribute(attributeName), out);
}
}
}
/* (non-Javadoc) */
void writeObject(Object obj, DataOutput out) throws IOException {
DataSerializer.writeObject(obj, out, allowJavaSerialization());
}
/* (non-Javadoc) */
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
synchronized (this.lock) {
for (int count = in.readInt(); count > 0; count--) {
setAttribute(in.readUTF(), readObject(in));
}
this.sessionAttributeDeltas.clear();
}
}
/* (non-Javadoc) */
<T> T readObject(DataInput in) throws ClassNotFoundException, IOException {
return DataSerializer.readObject(in);
}
/* (non-Javadoc) */
public boolean hasDelta() {
synchronized (this.lock) {
return !this.sessionAttributeDeltas.isEmpty();
}
}
/* (non-Javadoc) */
public void toDelta(DataOutput out) throws IOException {
synchronized (this.lock) {
out.writeInt(this.sessionAttributeDeltas.size());
for (Map.Entry<String, Object> entry : this.sessionAttributeDeltas.entrySet()) {
out.writeUTF(entry.getKey());
writeObject(entry.getValue(), out);
}
this.sessionAttributeDeltas.clear();
}
}
/* (non-Javadoc) */
public void fromDelta(DataInput in) throws InvalidDeltaException, IOException {
synchronized (this.lock) {
try {
int count = in.readInt();
Map<String, Object> deltas = new HashMap<String, Object>(count);
while (count-- > 0) {
deltas.put(in.readUTF(), readObject(in));
}
for (Map.Entry<String, Object> entry : deltas.entrySet()) {
setAttribute(entry.getKey(), entry.getValue());
this.sessionAttributeDeltas.remove(entry.getKey());
}
}
catch (ClassNotFoundException e) {
throw new InvalidDeltaException("class type in data not found", e);
}
}
}
@Override
public String toString() {
return this.sessionAttributes.toString();
}
}
/**
* GemFireSessionAttributesInstantiator is a GemFire {@link Instantiator} use to instantiate instances
* of the {@link GemFireSessionAttributes} object used in GemFire's data serialization framework when
* persisting Session attributes state in GemFire.
*/
public static class GemFireSessionAttributesInstantiator extends Instantiator {
public static GemFireSessionAttributesInstantiator create() {
return new GemFireSessionAttributesInstantiator(GemFireSessionAttributes.class, 800828008);
}
public GemFireSessionAttributesInstantiator(Class<? extends DataSerializable> type, int id) {
super(type, id);
}
@Override
public DataSerializable newInstance() {
return new GemFireSessionAttributes();
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire;
import java.util.HashMap;
import java.util.Map;
import org.apache.geode.cache.query.SelectResults;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
/**
* The {@link GemFireOperationsSessionRepository} class is a Spring {@link SessionRepository} implementation
* that interfaces with and uses GemFire to back and store Spring Sessions.
*
* @author John Blum
* @since 1.1.0
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository
*/
public class GemFireOperationsSessionRepository extends AbstractGemFireOperationsSessionRepository {
// GemFire OQL query used to lookup Sessions by arbitrary attributes.
protected static final String FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY = "SELECT s FROM %1$s s WHERE s.attributes['%2$s'] = $1";
// GemFire OQL query used to look up Sessions by principal name.
protected static final String FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY = "SELECT s FROM %1$s s WHERE s.principalName = $1";
/**
* Constructs an instance of GemFireOperationsSessionRepository initialized with the
* required GemfireOperations object used to perform data access operations to manage
* Session state.
*
* @param template the GemfireOperations object used to access and manage Session
* state in GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
*/
public GemFireOperationsSessionRepository(GemfireOperations template) {
super(template);
}
/**
* Looks up all available Sessions with the particular attribute indexed by name
* having the given value.
*
* @param indexName name of the indexed Session attribute. (e.g.
* {@link org.springframework.session.FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}
* ).
* @param indexValue value of the indexed Session attribute to search on (e.g.
* username).
* @return a mapping of Session ID to Session instances.
* @see org.springframework.session.ExpiringSession
* @see java.util.Map
* @see #prepareQuery(String)
*/
public Map<String, ExpiringSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
SelectResults<ExpiringSession> results = getTemplate().find(prepareQuery(indexName), indexValue);
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
for (ExpiringSession session : results.asList()) {
sessions.put(session.getId(), session);
}
return sessions;
}
/**
* Prepares the appropriate GemFire OQL query based on the indexed Session attribute
* name.
*
* @param indexName a String indicating the name of the indexed Session attribute.
* @return an appropriate GemFire OQL statement for querying on a particular indexed
* Session attribute.
*/
protected String prepareQuery(String indexName) {
return (PRINCIPAL_NAME_INDEX_NAME.equals(indexName)
? String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, getFullyQualifiedRegionName())
: String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY, getFullyQualifiedRegionName(), indexName));
}
/**
* Constructs a new {@link ExpiringSession} instance backed by GemFire.
*
* @return an instance of {@link ExpiringSession} backed by GemFire.
* @see AbstractGemFireOperationsSessionRepository.GemFireSession#create(int)
* @see org.springframework.session.ExpiringSession
* @see #getMaxInactiveIntervalInSeconds()
*/
public ExpiringSession createSession() {
return GemFireSession.create(getMaxInactiveIntervalInSeconds());
}
/**
* Gets a copy of an existing, non-expired {@link ExpiringSession} by ID. If the
* Session is expired, then it is deleted.
*
* @param sessionId a String indicating the ID of the Session to get.
* @return an existing {@link ExpiringSession} by ID or null if not Session exists.
* @see AbstractGemFireOperationsSessionRepository.GemFireSession#from(ExpiringSession)
* @see org.springframework.session.ExpiringSession
* @see #delete(String)
*/
public ExpiringSession getSession(String sessionId) {
ExpiringSession storedSession = getTemplate().get(sessionId);
if (storedSession != null) {
storedSession = storedSession.isExpired()
? delete(storedSession)
: touch(GemFireSession.from(storedSession));
}
return storedSession;
}
/**
* Saves the specified {@link ExpiringSession} to GemFire.
*
* @param session the {@link ExpiringSession} to save.
* @see org.springframework.data.gemfire.GemfireOperations#put(Object, Object)
* @see org.springframework.session.ExpiringSession
*/
public void save(ExpiringSession session) {
getTemplate().put(session.getId(), GemFireSession.from(session));
}
/**
* Deletes (removes) any existing {@link ExpiringSession} from GemFire. This operation
* also results in a SessionDeletedEvent.
*
* @param sessionId a String indicating the ID of the Session to remove from GemFire.
* @see org.springframework.data.gemfire.GemfireOperations#remove(Object)
* @see #handleDeleted(String, ExpiringSession)
*/
public void delete(String sessionId) {
handleDeleted(sessionId, getTemplate().<Object, ExpiringSession>remove(sessionId));
}
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Add this annotation to a Spring {@code @Configuration} class to expose the SessionRepositoryFilter
* as a bean named "springSessionRepositoryFilter" and backed by Pivotal GemFire or Apache Geode.
*
* In order to leverage the annotation, a single Pivotal GemFire/Apache Geode
* {@link org.apache.geode.cache.Cache}
* or {@link org.apache.geode.cache.client.ClientCache} instance must be provided.
*
* For example:
*
* <pre>
* <code>
* {@literal @Configuration}
* {@literal @EnableGemFireHttpSession}
* public class GemFirePeerCacheHttpSessionConfiguration {
*
* {@literal @Bean}
* public Properties gemfireProperties() {
* Properties gemfireProperties = new Properties();
* gemfireProperties.setProperty("name", "ExamplePeer");
* gemfireProperties.setProperty("mcast-port", "0");
* gemfireProperties.setProperty("log-level", "warning");
* return gemfireProperties;
* }
*
* {@literal @Bean}
* public CacheFactoryBean gemfireCache() throws Exception {
* CacheFactoryBean cache = new CacheFactoryBean();
* cache.setProperties(gemfireProperties());
* return cache;
* }
* }
* </code> </pre>
*
* Alternatively, Spring Session can be configured to use Pivotal GemFire (Apache Geode) as a client
* using a dedicated GemFire Server cluster and a {@link org.apache.geode.cache.client.ClientCache}.
*
* For example:
*
* <code>
* {@literal @Configuration}
* {@literal @EnableGemFireHttpSession}
* public class GemFireClientCacheHttpSessionConfiguration {
*
* {@literal @Bean}
* public Properties gemfireProperties() {
* Properties gemfireProperties = new Properties();
* gemfireProperties.setProperty("name", "ExampleClient");
* gemfireProperties.setProperty("log-level", "warning");
* return gemfireProperties;
* }
*
* {@literal @Bean}
* public ClientCacheFactoryBean gemfireCache() throws Exception {
* ClientCacheFactoryBean clientCache = new ClientCacheFactoryBean();
* clientCache.setClose(true)
* clientCache.setProperties(gemfireProperties());
* return clientCache;
* }
*
* {@literal @Bean}
* public PoolFactoryBean gemfirePool() {
* PoolFactoryBean pool = new PoolFactoryBean();
* pool.addServer(new ConnectionEndpoint("serverHost", 40404);
* return pool;
* }
* }
* </code>
*
* More advanced configurations can extend {@link GemFireHttpSessionConfiguration} instead.
*
* @author John Blum
* @see org.springframework.session.config.annotation.web.http.EnableSpringHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @since 1.1.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Configuration
@Import(GemFireHttpSessionConfiguration.class)
public @interface EnableGemFireHttpSession {
/**
* Defines the GemFire ClientCache Region DataPolicy.
*
* @return a ClientRegionShortcut used to specify and configure the ClientCache Region
* DataPolicy.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
ClientRegionShortcut clientRegionShortcut() default ClientRegionShortcut.PROXY;
/**
* Identifies the Session attributes by name that should be indexed for query
* operations. For instance, find all Sessions in GemFire having attribute A defined
* with value X.
*
* @return an array of Strings identifying the names of Session attributes to index.
*/
String[] indexableSessionAttributes() default {};
/**
* Defines the maximum interval in seconds that a Session can remain inactive before
* it is considered expired. Defaults to 1800 seconds, or 30 minutes.
*
* @return an integer value defining the maximum inactive interval in seconds for
* declaring a Session expired.
*/
int maxInactiveIntervalInSeconds() default 1800;
/**
* Specifies the name of the specific GemFire {@link org.apache.geode.cache.client.Pool} used
* by the Spring Session Data GemFire client Region ('ClusteredSpringSessions') when performing
* cache operations. This is attribute is only used in the client/server topology.
*
* @return the name of the GemFire {@link org.apache.geode.cache.client.Pool} to be used
* by the client Region used to manage (HTTP) Sessions.
* @see org.springframework.data.gemfire.config.xml.GemfireConstants#DEFAULT_GEMFIRE_POOL_NAME
*/
String poolName() default GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME;
/**
* Defines the name of the GemFire (Client)Cache Region used to store Sessions.
*
* @return a String specifying the name of the GemFire (Client)Cache Region used to
* store Sessions.
* @see org.apache.geode.cache.Region#getName()
*/
String regionName() default "ClusteredSpringSessions";
/**
* Defines the GemFire, Peer Cache Region DataPolicy.
*
* @return a RegionShortcut used to specify and configure the Peer Cache Region
* DataPolicy.
* @see org.apache.geode.cache.RegionShortcut
*/
RegionShortcut serverRegionShortcut() default RegionShortcut.PARTITION;
}

View File

@@ -1,487 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.IndexFactoryBean;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.session.ExpiringSession;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession;
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean;
import org.springframework.session.data.gemfire.config.annotation.web.http.support.SessionAttributesIndexFactoryBean;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.util.StringUtils;
/**
* The {@link GemFireHttpSessionConfiguration} class is a Spring {@link Configuration @Configuration} class
* used to configure and initialize Pivotal GemFire (or Apache Geode) as a clustered, replicated
* {@link javax.servlet.http.HttpSession} provider implementation in Spring {@link ExpiringSession}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.Pool
* @see org.springframework.beans.factory.BeanClassLoaderAware
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @since 1.1.0
*/
@Configuration
public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, ImportAware {
/**
* The default maximum interval in seconds in which a Session can remain inactive
* before it is considered expired.
*/
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS = (int) TimeUnit.MINUTES.toSeconds(30);
protected static final Class<Object> SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT = Object.class;
protected static final Class<GemFireSession> SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT = GemFireSession.class;
/**
* The default {@link ClientRegionShortcut} used to configure the GemFire ClientCache
* Region that will store Spring Sessions.
*/
public static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = ClientRegionShortcut.PROXY;
/**
* The default {@link RegionShortcut} used to configure the GemFire Cache Region that
* will store Spring Sessions.
*/
public static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT = RegionShortcut.PARTITION;
/**
* Name of the GemFire {@link Pool} used by the client Region for managing Session state information.
*/
public static final String DEFAULT_GEMFIRE_POOL_NAME = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
/**
* The default name of the Gemfire (Client)Cache Region used to store Sessions.
*/
public static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME = "ClusteredSpringSessions";
/**
* The default names of all Session attributes that should be indexed by GemFire.
*/
public static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = {};
private int maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
private ClassLoader beanClassLoader;
private ClientRegionShortcut clientRegionShortcut = DEFAULT_CLIENT_REGION_SHORTCUT;
private RegionShortcut serverRegionShortcut = DEFAULT_SERVER_REGION_SHORTCUT;
private String poolName = DEFAULT_GEMFIRE_POOL_NAME;
private String springSessionGemFireRegionName = DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES;
/**
* Sets a reference to the {@link ClassLoader} used to load bean definition class
* types in a Spring context.
*
* @param beanClassLoader the ClassLoader used by the Spring container to load bean
* class types.
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader)
* @see java.lang.ClassLoader
*/
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Gets a reference to the {@link ClassLoader} used to load bean definition class
* types in a Spring context.
*
* @return the ClassLoader used by the Spring container to load bean class types.
* @see java.lang.ClassLoader
*/
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
/**
* Sets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache
* Region that will store Spring Sessions.
*
* @param shortcut the ClientRegionShortcut used to configure the GemFire ClientCache
* Region.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
public void setClientRegionShortcut(ClientRegionShortcut shortcut) {
this.clientRegionShortcut = shortcut;
}
/**
* Gets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache
* Region that will store Spring Sessions. Defaults to
* {@link ClientRegionShortcut#PROXY}.
*
* @return the ClientRegionShortcut used to configure the GemFire ClientCache Region.
* @see org.apache.geode.cache.client.ClientRegionShortcut
* @see EnableGemFireHttpSession#clientRegionShortcut()
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (this.clientRegionShortcut != null ? this.clientRegionShortcut : DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Sets the names of all Session attributes that should be indexed by GemFire.
*
* @param indexableSessionAttributes an array of Strings indicating the names of all
* Session attributes for which an Index will be created by GemFire.
*/
public void setIndexableSessionAttributes(String[] indexableSessionAttributes) {
this.indexableSessionAttributes = indexableSessionAttributes;
}
/**
* Get the names of all Session attributes that should be indexed by GemFire.
*
* @return an array of Strings indicating the names of all Session attributes for
* which an Index will be created by GemFire. Defaults to an empty String array if
* unspecified.
* @see EnableGemFireHttpSession#indexableSessionAttributes()
*/
protected String[] getIndexableSessionAttributes() {
return (this.indexableSessionAttributes != null ? this.indexableSessionAttributes
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
}
/**
* Sets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum
* interval in seconds that a Session can remain inactive before it is considered
* expired.
*/
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
/**
* Gets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @return an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
* @see EnableGemFireHttpSession#maxInactiveIntervalInSeconds()
*/
protected int getMaxInactiveIntervalInSeconds() {
return this.maxInactiveIntervalInSeconds;
}
/**
* Sets the name of the GemFire {@link Pool} used by the client Region for managing Sessions
* during cache operations involving the server.
*
* @param poolName the name of a GemFire {@link Pool}.
* @see Pool#getName()
*/
public void setPoolName(String poolName) {
this.poolName = poolName;
}
/**
* Returns the name of the GemFire {@link Pool} used by the client Region for managing Sessions
* during cache operations involving the server.
*
* @return the name of a GemFire {@link Pool}.
* @see Pool#getName()
*/
protected String getPoolName() {
return (StringUtils.hasText(this.poolName) ? this.poolName : DEFAULT_GEMFIRE_POOL_NAME);
}
/**
* Sets the {@link RegionShortcut} used to configure the GemFire Cache Region that
* will store Spring Sessions.
*
* @param shortcut the RegionShortcut used to configure the GemFire Cache Region.
* @see org.apache.geode.cache.RegionShortcut
*/
public void setServerRegionShortcut(RegionShortcut shortcut) {
this.serverRegionShortcut = shortcut;
}
/**
* Gets the {@link RegionShortcut} used to configure the GemFire Cache Region that
* will store Spring Sessions. Defaults to {@link RegionShortcut#PARTITION}.
*
* @return the RegionShortcut used to configure the GemFire Cache Region.
* @see org.apache.geode.cache.RegionShortcut
* @see EnableGemFireHttpSession#serverRegionShortcut()
*/
protected RegionShortcut getServerRegionShortcut() {
return (this.serverRegionShortcut != null ? this.serverRegionShortcut : DEFAULT_SERVER_REGION_SHORTCUT);
}
/**
* Sets the name of the Gemfire (Client)Cache Region used to store Sessions.
*
* @param springSessionGemFireRegionName a String specifying the name of the GemFire
* (Client)Cache Region used to store the Session.
*/
public void setSpringSessionGemFireRegionName(String springSessionGemFireRegionName) {
this.springSessionGemFireRegionName = springSessionGemFireRegionName;
}
/**
* Gets the name of the Gemfire (Client)Cache Region used to store Sessions. Defaults
* to 'ClusteredSpringSessions'.
*
* @return a String specifying the name of the GemFire (Client)Cache Region used to
* store the Session.
* @see org.apache.geode.cache.Region#getName()
* @see EnableGemFireHttpSession#regionName()
*/
protected String getSpringSessionGemFireRegionName() {
return (StringUtils.hasText(this.springSessionGemFireRegionName) ? this.springSessionGemFireRegionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
/**
* Callback with the {@link AnnotationMetadata} of the class containing @Import
* annotation that imported this @Configuration class.
*
* @param importMetadata the AnnotationMetadata of the class importing
* this @Configuration class.
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes enableGemFireHttpSessionAnnotationAttributes =
AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(
EnableGemFireHttpSession.class.getName()));
setClientRegionShortcut(ClientRegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes
.getEnum("clientRegionShortcut")));
setIndexableSessionAttributes(enableGemFireHttpSessionAnnotationAttributes
.getStringArray("indexableSessionAttributes"));
setMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAnnotationAttributes
.getNumber("maxInactiveIntervalInSeconds").intValue());
setPoolName(enableGemFireHttpSessionAnnotationAttributes.getString("poolName"));
setServerRegionShortcut(RegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes
.getEnum("serverRegionShortcut")));
setSpringSessionGemFireRegionName(enableGemFireHttpSessionAnnotationAttributes
.getString("regionName"));
}
/**
* Defines the Spring SessionRepository bean used to interact with GemFire as a Spring
* Session provider.
*
* @param gemfireOperations an instance of {@link GemfireOperations} used to manage
* Spring Sessions in GemFire.
* @return a GemFireOperationsSessionRepository for managing (clustering/replicating)
* Sessions using GemFire.
*/
@Bean
public GemFireOperationsSessionRepository sessionRepository(
@Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) {
GemFireOperationsSessionRepository sessionRepository =
new GemFireOperationsSessionRepository(gemfireOperations);
sessionRepository.setMaxInactiveIntervalInSeconds(getMaxInactiveIntervalInSeconds());
return sessionRepository;
}
/**
* Defines a Spring GemfireTemplate bean used to interact with GemFire's (Client)Cache
* {@link Region} storing Sessions.
*
* @param gemFireCache reference to the single GemFire cache instance used by the
* {@link GemfireTemplate} to perform GemFire cache data access operations.
* @return a {@link GemfireTemplate} used to interact with GemFire's (Client)Cache
* {@link Region} storing Sessions.
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.apache.geode.cache.Region
*/
@Bean
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public GemfireTemplate sessionRegionTemplate(GemFireCache gemFireCache) {
return new GemfireTemplate(gemFireCache.getRegion(getSpringSessionGemFireRegionName()));
}
/**
* Defines a Spring GemFire {@link org.apache.geode.cache.Cache} {@link Region}
* bean used to store and manage Sessions using either a client-server or peer-to-peer
* (p2p) topology.
*
* @param gemfireCache a reference to the GemFire
* {@link org.apache.geode.cache.Cache}.
* @param sessionRegionAttributes the GemFire {@link RegionAttributes} used to
* configure the {@link Region}.
* @return a {@link GemFireCacheTypeAwareRegionFactoryBean} used to configure and
* initialize a GemFire Cache {@link Region} for storing and managing Sessions.
* @see #getClientRegionShortcut()
* @see #getSpringSessionGemFireRegionName()
* @see #getServerRegionShortcut()
*/
@Bean(name = DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion(GemFireCache gemfireCache,
@Qualifier("sessionRegionAttributes") RegionAttributes<Object, ExpiringSession> sessionRegionAttributes) {
GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion =
new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>();
sessionRegion.setClientRegionShortcut(getClientRegionShortcut());
sessionRegion.setGemfireCache(gemfireCache);
sessionRegion.setPoolName(getPoolName());
sessionRegion.setRegionAttributes(sessionRegionAttributes);
sessionRegion.setRegionName(getSpringSessionGemFireRegionName());
sessionRegion.setServerRegionShortcut(getServerRegionShortcut());
return sessionRegion;
}
/**
* Defines a Spring GemFire {@link RegionAttributes} bean used to configure and
* initialize the GemFire cache {@link Region} storing Sessions. Expiration is also
* configured for the {@link Region} on the basis that the GemFire cache
* {@link Region} is a not a proxy, on either the client or server.
*
* @param gemfireCache a reference to the GemFire cache.
* @return an instance of {@link RegionAttributes} used to configure and initialize
* the GemFire cache {@link Region} for storing and managing Sessions.
* @see org.springframework.data.gemfire.RegionAttributesFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.PartitionAttributes
* @see #isExpirationAllowed(GemFireCache)
*/
@Bean
@SuppressWarnings({ "unchecked", "deprecation" })
public RegionAttributesFactoryBean sessionRegionAttributes(GemFireCache gemfireCache) {
RegionAttributesFactoryBean regionAttributes = new RegionAttributesFactoryBean();
regionAttributes.setKeyConstraint(SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT);
regionAttributes.setValueConstraint(SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT);
if (isExpirationAllowed(gemfireCache)) {
regionAttributes.setStatisticsEnabled(true);
regionAttributes.setEntryIdleTimeout(
new ExpirationAttributes(Math.max(getMaxInactiveIntervalInSeconds(), 0), ExpirationAction.INVALIDATE));
}
return regionAttributes;
}
/**
* Determines whether expiration configuration is allowed to be set on the GemFire
* cache {@link Region} used to store and manage Sessions.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a boolean indicating if a {@link Region} can be configured for Region entry
* idle-timeout expiration.
* @see GemFireUtils#isClient(GemFireCache)
* @see GemFireUtils#isProxy(ClientRegionShortcut)
* @see GemFireUtils#isProxy(RegionShortcut)
*/
boolean isExpirationAllowed(GemFireCache gemfireCache) {
return !(GemFireUtils.isClient(gemfireCache) ? GemFireUtils.isProxy(getClientRegionShortcut())
: GemFireUtils.isProxy(getServerRegionShortcut()));
}
/**
* Defines a GemFire Index bean on the GemFire cache {@link Region} storing and managing Sessions,
* specifically on the 'principalName' property for quick lookup of Sessions by 'principalName'.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a {@link IndexFactoryBean} to create an GemFire Index on the 'principalName' property
* for Sessions stored in the GemFire cache {@link Region}.
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see org.apache.geode.cache.GemFireCache
*/
@Bean
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public IndexFactoryBean principalNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean index = new IndexFactoryBean();
index.setCache(gemfireCache);
index.setName("principalNameIndex");
index.setExpression("principalName");
index.setFrom(GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName()));
index.setOverride(true);
index.setType(IndexType.HASH);
return index;
}
/**
* Defines a GemFire Index bean on the GemFire cache {@link Region} storing and managing Sessions,
* specifically on all Session attributes for quick lookup and queries on Session attribute names
* with a given value.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a {@link IndexFactoryBean} to create an GemFire Index on attributes of Sessions
* stored in the GemFire cache {@link Region}.
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see org.apache.geode.cache.GemFireCache
*/
@Bean
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public SessionAttributesIndexFactoryBean sessionAttributesIndex(GemFireCache gemfireCache) {
SessionAttributesIndexFactoryBean sessionAttributesIndex = new SessionAttributesIndexFactoryBean();
sessionAttributesIndex.setGemFireCache(gemfireCache);
sessionAttributesIndex.setIndexableSessionAttributes(getIndexableSessionAttributes());
sessionAttributesIndex.setRegionName(getSpringSessionGemFireRegionName());
return sessionAttributesIndex;
}
}

View File

@@ -1,384 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http.support;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.InterestResultPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.GenericRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.client.Interest;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link GemFireCacheTypeAwareRegionFactoryBean} class is a Spring {@link FactoryBean}
* used to construct, configure and initialize the GemFire cache {@link Region} used to
* store and manage Session state.
*
* @param <K> the type of keys
* @param <V> the type of values
* @author John Blum
* @since 1.1.0
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
*/
public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
implements BeanFactoryAware, FactoryBean<Region<K, V>>, InitializingBean {
protected static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT =
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT;
protected static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT =
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT;
protected static final String DEFAULT_GEMFIRE_POOL_NAME =
GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME;
protected static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME =
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
private BeanFactory beanFactory;
private ClientRegionShortcut clientRegionShortcut;
private GemFireCache gemfireCache;
private Region<K, V> region;
private RegionAttributes<K, V> regionAttributes;
private RegionShortcut serverRegionShortcut;
private String poolName;
private String regionName;
/**
* Post-construction initialization callback to create, configure and initialize the
* GemFire cache {@link Region} used to store, replicate (distribute) and manage
* Session state. This method intelligently handles both client-server and
* peer-to-peer (p2p) GemFire supported distributed system topologies.
*
* @throws Exception if the initialization of the GemFire cache {@link Region} fails.
* @see org.springframework.session.data.gemfire.support.GemFireUtils#isClient(GemFireCache)
* @see #getGemfireCache()
* @see #newClientRegion(GemFireCache)
* @see #newServerRegion(GemFireCache)
*/
public void afterPropertiesSet() throws Exception {
GemFireCache gemfireCache = getGemfireCache();
this.region = (GemFireUtils.isClient(gemfireCache) ? newClientRegion(gemfireCache)
: newServerRegion(gemfireCache));
}
/**
* Constructs a GemFire cache {@link Region} using a peer-to-peer (p2p) GemFire
* topology to store and manage Session state in a GemFire server cluster accessible
* from a GemFire cache client.
*
* @param gemfireCache a reference to the GemFire
* {@link org.apache.geode.cache.Cache}.
* @return a peer-to-peer-based GemFire cache {@link Region} to store and manage
* Session state.
* @throws Exception if the instantiation, configuration and initialization of the
* GemFire cache {@link Region} fails.
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see #getRegionAttributes()
* @see #getRegionName()
* @see #getServerRegionShortcut()
*/
protected Region<K, V> newServerRegion(GemFireCache gemfireCache) throws Exception {
GenericRegionFactoryBean<K, V> serverRegion = new GenericRegionFactoryBean<K, V>();
serverRegion.setAttributes(getRegionAttributes());
serverRegion.setCache(gemfireCache);
serverRegion.setRegionName(getRegionName());
serverRegion.setShortcut(getServerRegionShortcut());
serverRegion.afterPropertiesSet();
return serverRegion.getObject();
}
/**
* Constructs a GemFire cache {@link Region} using the client-server GemFire topology
* to store and manage Session state in a GemFire server cluster accessible from a
* GemFire cache client.
*
* @param gemfireCache a reference to the GemFire
* {@link org.apache.geode.cache.Cache}.
* @return a client-server-based GemFire cache {@link Region} to store and manage
* Session state.
* @throws Exception if the instantiation, configuration and initialization of the
* GemFire cache {@link Region} fails.
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see #getClientRegionShortcut()
* @see #getRegionAttributes()
* @see #getRegionName()
* @see #registerInterests(boolean)
*/
protected Region<K, V> newClientRegion(GemFireCache gemfireCache) throws Exception {
ClientRegionFactoryBean<K, V> clientRegion = new ClientRegionFactoryBean<K, V>();
ClientRegionShortcut shortcut = getClientRegionShortcut();
clientRegion.setAttributes(getRegionAttributes());
clientRegion.setBeanFactory(getBeanFactory());
clientRegion.setCache(gemfireCache);
clientRegion.setInterests(registerInterests(!GemFireUtils.isLocal(shortcut)));
clientRegion.setPoolName(getPoolName());
clientRegion.setRegionName(getRegionName());
clientRegion.setShortcut(shortcut);
clientRegion.afterPropertiesSet();
return clientRegion.getObject();
}
/**
* Decides whether interests will be registered for all keys. Interests is only registered on
* a client and typically only when the client is a (CACHING) PROXY to the server (i.e. non-LOCAL only).
*
* @param register a boolean value indicating whether interests should be registered.
* @return an array of Interests KEY/VALUE registrations.
* @see org.springframework.data.gemfire.client.Interest
*/
@SuppressWarnings("unchecked")
protected Interest<K>[] registerInterests(boolean register) {
return (!register ? new Interest[0] : new Interest[] {
new Interest<String>("ALL_KEYS", InterestResultPolicy.KEYS)
});
}
/**
* Returns a reference to the constructed GemFire cache {@link Region} used to store
* and manage Session state.
*
* @return the {@link Region} used to store and manage Session state.
* @throws Exception if the {@link Region} reference cannot be obtained.
* @see org.apache.geode.cache.Region
*/
public Region<K, V> getObject() throws Exception {
return this.region;
}
/**
* Returns the specific type of GemFire cache {@link Region} this factory creates when
* initialized or Region.class when uninitialized.
*
* @return the GemFire cache {@link Region} class type constructed by this factory.
* @see org.apache.geode.cache.Region
* @see java.lang.Class
*/
public Class<?> getObjectType() {
return (this.region != null ? this.region.getClass() : Region.class);
}
/**
* Returns true indicating the GemFire cache {@link Region} created by this factory is
* the sole instance.
*
* @return true to indicate the GemFire cache {@link Region} storing and managing
* Sessions is a Singleton.
*/
public boolean isSingleton() {
return true;
}
/**
* Sets a reference to the Spring {@link BeanFactory} responsible for
* creating GemFire components.
*
* @param beanFactory reference to the Spring {@link BeanFactory}
* @throws IllegalArgumentException if the {@link BeanFactory} reference is null.
* @see org.springframework.beans.factory.BeanFactory
*/
public void setBeanFactory(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
/**
* Gets a reference to the Spring {@link BeanFactory} responsible for
* creating GemFire components.
*
* @return a reference to the Spring {@link BeanFactory}
* @throws IllegalStateException if the {@link BeanFactory} reference
* is null.
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "A reference to the BeanFactory was not properly configured");
return this.beanFactory;
}
/**
* Sets the {@link Region} data policy used by the GemFire cache client to manage
* Session state.
*
* @param clientRegionShortcut a {@link ClientRegionShortcut} to specify the client
* {@link Region} data management policy.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
public void setClientRegionShortcut(ClientRegionShortcut clientRegionShortcut) {
this.clientRegionShortcut = clientRegionShortcut;
}
/**
* Returns the {@link Region} data policy used by the GemFire cache client to manage
* Session state. Defaults to {@link ClientRegionShortcut#PROXY}.
*
* @return a {@link ClientRegionShortcut} specifying the client {@link Region} data
* management policy.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration#DEFAULT_CLIENT_REGION_SHORTCUT
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (this.clientRegionShortcut != null ? this.clientRegionShortcut : DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Sets a reference to the GemFire cache used to construct the appropriate
* {@link Region}.
*
* @param gemfireCache a reference to the GemFire cache.
* @throws IllegalArgumentException if the {@link GemFireCache} reference is null.
*/
public void setGemfireCache(GemFireCache gemfireCache) {
Assert.notNull(gemfireCache, "GemFireCache must not be null");
this.gemfireCache = gemfireCache;
}
/**
* Returns a reference to the GemFire cache used to construct the appropriate
* {@link Region}.
*
* @return a reference to the GemFire cache.
* @throws IllegalStateException if the {@link GemFireCache} reference is null.
*/
protected GemFireCache getGemfireCache() {
Assert.state(this.gemfireCache != null, "A reference to the GemFireCache was not properly configured");
return this.gemfireCache;
}
/**
* Sets the name of the GemFire {@link Pool} used by the client Region for managing Sessions
* during cache operations involving the server.
*
* @param poolName the name of a GemFire {@link Pool}.
* @see Pool#getName()
*/
public void setPoolName(final String poolName) {
this.poolName = poolName;
}
/**
* Returns the name of the GemFire {@link Pool} used by the client Region for managing Sessions
* during cache operations involving the server.
*
* @return the name of a GemFire {@link Pool}.
* @see Pool#getName()
*/
protected String getPoolName() {
return (StringUtils.hasText(this.poolName) ? this.poolName : DEFAULT_GEMFIRE_POOL_NAME);
}
/**
* Sets the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region} used to store and manage Session state.
*
* @param regionAttributes the GemFire {@link RegionAttributes} used to configure the
* GemFire cache {@link Region}.
* @see org.apache.geode.cache.RegionAttributes
*/
public void setRegionAttributes(RegionAttributes<K, V> regionAttributes) {
this.regionAttributes = regionAttributes;
}
/**
* Returns the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region} used to store and manage Session state.
*
* @return the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region}.
* @see org.apache.geode.cache.RegionAttributes
*/
protected RegionAttributes<K, V> getRegionAttributes() {
return this.regionAttributes;
}
/**
* Sets the name of the GemFire cache {@link Region} use to store and manage Session
* state.
*
* @param regionName a String specifying the name of the GemFire cache {@link Region}.
*/
public void setRegionName(final String regionName) {
this.regionName = regionName;
}
/**
* Returns the configured name of the GemFire cache {@link Region} use to store and
* manage Session state. Defaults to "ClusteredSpringSessions"
*
* @return a String specifying the name of the GemFire cache {@link Region}.
* @see org.apache.geode.cache.Region#getName()
*/
protected String getRegionName() {
return (StringUtils.hasText(this.regionName) ? this.regionName : DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
/**
* Sets the {@link Region} data policy used by the GemFire peer cache to manage
* Session state.
*
* @param serverRegionShortcut a {@link RegionShortcut} to specify the peer
* {@link Region} data management policy.
* @see org.apache.geode.cache.RegionShortcut
*/
public void setServerRegionShortcut(RegionShortcut serverRegionShortcut) {
this.serverRegionShortcut = serverRegionShortcut;
}
/**
* Returns the {@link Region} data policy used by the GemFire peer cache to manage
* Session state. Defaults to {@link RegionShortcut#PARTITION}.
*
* @return a {@link RegionShortcut} specifying the peer {@link Region} data management
* policy.
* @see org.apache.geode.cache.RegionShortcut
*/
protected RegionShortcut getServerRegionShortcut() {
return (this.serverRegionShortcut != null ? this.serverRegionShortcut : DEFAULT_SERVER_REGION_SHORTCUT);
}
}

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http.support;
import javax.servlet.http.HttpSession;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.query.Index;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.IndexFactoryBean;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.util.ObjectUtils;
/**
* The SessionAttributesIndexFactoryBean class is a Spring {@link FactoryBean} that creates a GemFire {@link Index}
* on the session attributes of the {@link HttpSession} object.
*
* @author John Blum
* @since 1.3.0
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.BeanNameAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see org.apache.geode.cache.query.Index
*/
public class SessionAttributesIndexFactoryBean implements FactoryBean<Index>,
InitializingBean, BeanFactoryAware, BeanNameAware {
protected static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = {};
private BeanFactory beanFactory;
private GemFireCache gemfireCache;
private Index sessionAttributesIndex;
private String beanName;
private String regionName;
private String[] indexableSessionAttributes;
/* (non-Javadoc) */
public void afterPropertiesSet() throws Exception {
if (isIndexableSessionAttributesConfigured()) {
this.sessionAttributesIndex = newIndex();
}
}
/**
* Determines whether any indexable Session attributes were configured for this {@link FactoryBean}.
*
* @return a boolean value indicating whether any indexable Session attributes were configured
* for this {@link FactoryBean}
* @see #setIndexableSessionAttributes(String[])
*/
protected boolean isIndexableSessionAttributesConfigured() {
return !ObjectUtils.isEmpty(this.indexableSessionAttributes);
}
/**
* Constructs a GemFire {@link Index} over the attributes of the {@link HttpSession}.
*
* @return a GemFire {@link Index} over the {@link HttpSession} attributes.
* @throws Exception if an error occurs while initializing the GemFire {@link Index}.
* @see org.springframework.data.gemfire.IndexFactoryBean
*/
protected Index newIndex() throws Exception {
IndexFactoryBean indexFactory = new IndexFactoryBean();
indexFactory.setBeanFactory(this.beanFactory);
indexFactory.setBeanName(this.beanName);
indexFactory.setCache(this.gemfireCache);
indexFactory.setName("sessionAttributesIndex");
indexFactory.setExpression(String.format("s.attributes[%1$s]",
getIndexableSessionAttributesAsGemFireIndexExpression()));
indexFactory.setFrom(String.format("%1$s s", GemFireUtils.toRegionPath(this.regionName)));
indexFactory.setOverride(true);
indexFactory.afterPropertiesSet();
return indexFactory.getObject();
}
/**
* Gets the names of all Session attributes that will be indexed by GemFire as single, comma-delimited
* String value constituting the Index expression of the Index definition.
*
* @return a String composed of all the named Session attributes for which GemFire
* will create an Index as an Index definition expression. If the indexable Session
* attributes were not configured, then the wildcard ("*") is returned.
* @see org.apache.geode.cache.query.Index#getIndexedExpression()
*/
protected String getIndexableSessionAttributesAsGemFireIndexExpression() {
StringBuilder builder = new StringBuilder();
for (String sessionAttribute : getIndexableSessionAttributes()) {
builder.append(builder.length() > 0 ? ", " : "");
builder.append(String.format("'%s'", sessionAttribute));
}
String indexExpression = builder.toString();
return (indexExpression.isEmpty() ? "*" : indexExpression);
}
/* (non-Javadoc) */
public Index getObject() throws Exception {
return this.sessionAttributesIndex;
}
/* (non-Javadoc) */
public Class<?> getObjectType() {
return (this.sessionAttributesIndex != null ? this.sessionAttributesIndex.getClass() : Index.class);
}
/* (non-Javadoc) */
public boolean isSingleton() {
return true;
}
/* (non-Javadoc) */
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/* (non-Javadoc) */
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/* (non-Javadoc) */
public void setGemFireCache(GemFireCache gemfireCache) {
this.gemfireCache = gemfireCache;
}
/* (non-Javadoc) */
public void setIndexableSessionAttributes(String[] indexableSessionAttributes) {
this.indexableSessionAttributes = indexableSessionAttributes;
}
/* (non-Javadoc) */
protected String[] getIndexableSessionAttributes() {
return (this.indexableSessionAttributes != null ? this.indexableSessionAttributes
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
}
/* (non-Javadoc) */
public void setRegionName(String regionName) {
this.regionName = regionName;
}
/* (non-Javadoc) */
protected String getRegionName() {
return this.regionName;
}
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.support;
import java.io.Closeable;
import java.io.IOException;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.internal.cache.GemFireCacheImpl;
/**
* GemFireUtils is an abstract, extensible utility class for working with GemFire types
* and functionality and is used by Spring Session's GemFire adapter support classes.
*
* @author John Blum
* @since 1.1.0
*/
public abstract class GemFireUtils {
/**
* Null-safe method to close the given {@link Closeable} object.
*
* @param obj the {@link Closeable} object to close.
* @return true if the {@link Closeable} object is not null and was successfully
* closed, otherwise return false.
* @see java.io.Closeable
*/
public static boolean close(Closeable obj) {
if (obj != null) {
try {
obj.close();
return true;
}
catch (IOException ignore) {
}
}
return false;
}
/**
* Determines whether the GemFire cache is a client.
*
* @param gemFireCache a reference to the GemFire cache.
* @return a boolean value indicating whether the GemFire cache is a client.
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.GemFireCache
*/
public static boolean isClient(GemFireCache gemFireCache) {
boolean client = (gemFireCache instanceof ClientCache);
client &= (!(gemFireCache instanceof GemFireCacheImpl) || ((GemFireCacheImpl) gemFireCache).isClient());
return client;
}
/**
* Determines whether the GemFire cache is a peer.
*
* @param gemFireCache a reference to the GemFire cache.
* @return a boolean value indicating whether the GemFire cache is a peer.
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
*/
public static boolean isPeer(GemFireCache gemFireCache) {
return (gemFireCache instanceof Cache && !isClient(gemFireCache));
}
/**
* Determines whether the given {@link ClientRegionShortcut} is local only.
*
* @param shortcut the ClientRegionShortcut to evaluate.
* @return a boolean value indicating if the {@link ClientRegionShortcut} is local or
* not.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
public static boolean isLocal(ClientRegionShortcut shortcut) {
switch (shortcut) {
case LOCAL:
case LOCAL_HEAP_LRU:
case LOCAL_OVERFLOW:
case LOCAL_PERSISTENT:
case LOCAL_PERSISTENT_OVERFLOW:
return true;
default:
return false;
}
}
/**
* Determines whether the client {@link ClientRegionShortcut} is a proxy-based
* shortcut. NOTE: "proxy"-based Regions keep no local state.
*
* @param shortcut the client {@link ClientRegionShortcut} to evaluate.
* @return a boolean value indicating whether the client {@link ClientRegionShortcut}
* refers to a proxy-based shortcut.
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
public static boolean isProxy(ClientRegionShortcut shortcut) {
switch (shortcut) {
case PROXY:
return true;
default:
return false;
}
}
/**
* Determines whether the given {@link Region} is a PROXY, which would be indicated by the {@link Region}
* having a {@link DataPolicy} of {@link DataPolicy#EMPTY}.
*
* @param region {@link Region} to evaluate.
* @return a boolean value indicating whether the {@link Region} is a PROXY.
* @see org.apache.geode.cache.DataPolicy
* @see org.apache.geode.cache.Region
*/
public static boolean isProxy(Region<?, ?> region) {
return DataPolicy.EMPTY.equals(region.getAttributes().getDataPolicy());
}
/**
* Determines whether the peer {@link RegionShortcut} is a proxy-based shortcut. NOTE:
* "proxy"-based Regions keep no local state.
*
* @param shortcut the peer {@link RegionShortcut} to evaluate.
* @return a boolean value indicating whether the peer {@link RegionShortcut} refers
* to a proxy-based shortcut.
* @see org.apache.geode.cache.RegionShortcut
*/
public static boolean isProxy(RegionShortcut shortcut) {
switch (shortcut) {
case PARTITION_PROXY:
case PARTITION_PROXY_REDUNDANT:
case REPLICATE_PROXY:
return true;
default:
return false;
}
}
/**
* Converts a {@link Region} name to a {@link Region} path.
*
* @param regionName a String specifying the name of the {@link Region}.
* @return a String path for the given {@link Region} by name.
* @see org.apache.geode.cache.Region#getFullPath()
* @see org.apache.geode.cache.Region#getName()
*/
public static String toRegionPath(String regionName) {
return String.format("%1$s%2$s", Region.SEPARATOR, regionName);
}
}

View File

@@ -1,436 +0,0 @@
/*
* Copyright 2014-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 org.springframework.session.data.gemfire;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.geode.cache.AttributesMutator;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.SelectResults;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.gemfire.GemfireAccessor;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionDeletedEvent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* The GemFireOperationsSessionRepositoryTest class is a test suite of test cases testing
* the contract and functionality of the GemFireOperationsSessionRepository class.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.apache.geode.cache.Region
*/
@RunWith(MockitoJUnitRunner.class)
public class GemFireOperationsSessionRepositoryTest {
protected static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
@Mock
private ApplicationEventPublisher mockApplicationEventPublisher;
@Mock
private AttributesMutator<Object, ExpiringSession> mockAttributesMutator;
@Mock
private GemfireOperationsAccessor mockTemplate;
private GemFireOperationsSessionRepository sessionRepository;
@Mock
private Region<Object, ExpiringSession> mockRegion;
@Before
public void setup() throws Exception {
given(this.mockRegion.getAttributesMutator()).willReturn(this.mockAttributesMutator);
given(this.mockRegion.getFullPath()).willReturn("/Example");
given(this.mockTemplate.<Object, ExpiringSession>getRegion()).willReturn(this.mockRegion);
this.sessionRepository = new GemFireOperationsSessionRepository(this.mockTemplate);
this.sessionRepository.setApplicationEventPublisher(this.mockApplicationEventPublisher);
this.sessionRepository.setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.sessionRepository.afterPropertiesSet();
assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(this.mockApplicationEventPublisher);
assertThat(this.sessionRepository.getFullyQualifiedRegionName()).isEqualTo("/Example");
assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@After
public void tearDown() {
verify(this.mockAttributesMutator, times(1)).addCacheListener(same(this.sessionRepository));
verify(this.mockRegion, times(1)).getFullPath();
verify(this.mockTemplate, times(1)).getRegion();
}
@Test
@SuppressWarnings("unchecked")
public void findByIndexNameValueFindsMatchingSession() {
ExpiringSession mockSession = mock(ExpiringSession.class, "MockSession");
given(mockSession.getId()).willReturn("1");
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
given(mockSelectResults.asList()).willReturn(Collections.<Object>singletonList(mockSession));
String indexName = "vip";
String indexValue = "rwinch";
String expectedQql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
this.sessionRepository.getFullyQualifiedRegionName(), indexName);
given(this.mockTemplate.find(eq(expectedQql), eq(indexValue))).willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions =
this.sessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(1);
assertThat(sessions.get("1")).isEqualTo(mockSession);
verify(this.mockTemplate, times(1)).find(eq(expectedQql), eq(indexValue));
verify(mockSelectResults, times(1)).asList();
verify(mockSession, times(1)).getId();
}
@Test
@SuppressWarnings("unchecked")
public void findByPrincipalNameFindsMatchingSessions() throws Exception {
ExpiringSession mockSessionOne = mock(ExpiringSession.class, "MockSessionOne");
ExpiringSession mockSessionTwo = mock(ExpiringSession.class, "MockSessionTwo");
ExpiringSession mockSessionThree = mock(ExpiringSession.class, "MockSessionThree");
given(mockSessionOne.getId()).willReturn("1");
given(mockSessionTwo.getId()).willReturn("2");
given(mockSessionThree.getId()).willReturn("3");
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
given(mockSelectResults.asList())
.willReturn(Arrays.<Object>asList(mockSessionOne, mockSessionTwo, mockSessionThree));
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
given(this.mockTemplate.find(eq(expectedOql), eq(principalName))).willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(3);
assertThat(sessions.get("1")).isEqualTo(mockSessionOne);
assertThat(sessions.get("2")).isEqualTo(mockSessionTwo);
assertThat(sessions.get("3")).isEqualTo(mockSessionThree);
verify(this.mockTemplate, times(1)).find(eq(expectedOql), eq(principalName));
verify(mockSelectResults, times(1)).asList();
verify(mockSessionOne, times(1)).getId();
verify(mockSessionTwo, times(1)).getId();
verify(mockSessionThree, times(1)).getId();
}
@Test
@SuppressWarnings("unchecked")
public void findByPrincipalNameReturnsNoMatchingSessions() {
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
given(mockSelectResults.asList()).willReturn(Collections.emptyList());
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
given(this.mockTemplate.find(eq(expectedOql), eq(principalName))).willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.isEmpty()).isTrue();
verify(this.mockTemplate, times(1)).find(eq(expectedOql), eq(principalName));
verify(mockSelectResults, times(1)).asList();
}
@Test
public void prepareQueryReturnsPrincipalNameOql() {
String actualQql =
this.sessionRepository.prepareQuery(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
assertThat(actualQql).isEqualTo(expectedOql);
}
@Test
public void prepareQueryReturnsIndexNameValueOql() {
String attributeName = "testAttributeName";
String actualOql = this.sessionRepository.prepareQuery(attributeName);
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
this.sessionRepository.getFullyQualifiedRegionName(), attributeName);
assertThat(actualOql).isEqualTo(expectedOql);
}
@Test
public void createProperlyInitializedSession() {
long beforeOrAtCreationTime = System.currentTimeMillis();
ExpiringSession session = this.sessionRepository.createSession();
assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(session.getId()).isNotNull();
assertThat(session.getAttributeNames().isEmpty()).isTrue();
assertThat(session.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getLastAccessedTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@Test
public void getSessionDeletesMatchingExpiredSessionById() {
final String expectedSessionId = "1";
final ExpiringSession mockSession = mock(ExpiringSession.class);
given(mockSession.isExpired()).willReturn(true);
given(mockSession.getId()).willReturn(expectedSessionId);
given(this.mockTemplate.get(eq(expectedSessionId))).willReturn(mockSession);
given(this.mockTemplate.remove(eq(expectedSessionId))).willReturn(mockSession);
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgument(0);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource())
.isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.<ExpiringSession>getSession()).isSameAs(mockSession);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(any(ApplicationEvent.class));
assertThat(this.sessionRepository.getSession(expectedSessionId)).isNull();
verify(this.mockTemplate, times(1)).get(eq(expectedSessionId));
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(mockSession, times(1)).isExpired();
verify(mockSession, times(2)).getId();
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
@Test
public void getSessionFindsMatchingNonExpiredSessionById() {
String expectedId = "1";
long expectedCreationTime = System.currentTimeMillis();
long currentLastAccessedTime = (expectedCreationTime + TimeUnit.MINUTES.toMillis(5));
ExpiringSession mockSession = mock(ExpiringSession.class);
given(mockSession.isExpired()).willReturn(false);
given(mockSession.getId()).willReturn(expectedId);
given(mockSession.getCreationTime()).willReturn(expectedCreationTime);
given(mockSession.getLastAccessedTime()).willReturn(currentLastAccessedTime);
given(mockSession.getAttributeNames()).willReturn(Collections.singleton("attrOne"));
given(mockSession.getAttribute(eq("attrOne"))).willReturn("test");
given(this.mockTemplate.get(eq(expectedId))).willReturn(mockSession);
ExpiringSession actualSession = this.sessionRepository.getSession(expectedId);
assertThat(actualSession).isNotSameAs(mockSession);
assertThat(actualSession.getId()).isEqualTo(expectedId);
assertThat(actualSession.getCreationTime()).isEqualTo(expectedCreationTime);
assertThat(actualSession.getLastAccessedTime()).isNotEqualTo(currentLastAccessedTime);
assertThat(actualSession.getLastAccessedTime()).isGreaterThanOrEqualTo(expectedCreationTime);
assertThat(actualSession.getAttributeNames()).isEqualTo(Collections.singleton("attrOne"));
assertThat(String.valueOf(actualSession.<String>getAttribute("attrOne"))).isEqualTo("test");
verify(this.mockTemplate, times(1)).get(eq(expectedId));
verify(mockSession, times(1)).isExpired();
verify(mockSession, times(1)).getId();
verify(mockSession, times(1)).getCreationTime();
verify(mockSession, times(1)).getLastAccessedTime();
verify(mockSession, times(1)).getAttributeNames();
verify(mockSession, times(1)).getAttribute(eq("attrOne"));
}
@Test
public void getSessionReturnsNull() {
given(this.mockTemplate.get(anyString())).willReturn(null);
assertThat(this.sessionRepository.getSession("1")).isNull();
}
@Test
public void saveStoresSession() {
final String expectedSessionId = "1";
final long expectedCreationTime = System.currentTimeMillis();
final long expectedLastAccessTime = (expectedCreationTime + TimeUnit.MINUTES.toMillis(5));
ExpiringSession mockSession = mock(ExpiringSession.class);
given(mockSession.getId()).willReturn(expectedSessionId);
given(mockSession.getCreationTime()).willReturn(expectedCreationTime);
given(mockSession.getLastAccessedTime()).willReturn(expectedLastAccessTime);
given(mockSession.getMaxInactiveIntervalInSeconds())
.willReturn(MAX_INACTIVE_INTERVAL_IN_SECONDS);
given(mockSession.getAttributeNames()).willReturn(Collections.<String>emptySet());
given(this.mockTemplate.put(eq(expectedSessionId),
isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class)))
.willAnswer(new Answer<ExpiringSession>() {
public ExpiringSession answer(InvocationOnMock invocation) throws Throwable {
ExpiringSession session = invocation.getArgument(1);
assertThat(session).isNotNull();
assertThat(session.getId()).isEqualTo(expectedSessionId);
assertThat(session.getCreationTime()).isEqualTo(expectedCreationTime);
assertThat(session.getLastAccessedTime()).isEqualTo(expectedLastAccessTime);
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(session.getAttributeNames().isEmpty()).isTrue();
return null;
}
});
this.sessionRepository.save(mockSession);
verify(mockSession, times(2)).getId();
verify(mockSession, times(1)).getCreationTime();
verify(mockSession, times(1)).getLastAccessedTime();
verify(mockSession, times(1)).getMaxInactiveIntervalInSeconds();
verify(mockSession, times(1)).getAttributeNames();
verify(this.mockTemplate, times(1)).put(eq(expectedSessionId),
isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class));
}
@Test
public void deleteRemovesExistingSessionAndHandlesDelete() {
final String expectedSessionId = "1";
final ExpiringSession mockSession = mock(ExpiringSession.class);
given(mockSession.getId()).willReturn(expectedSessionId);
given(this.mockTemplate.remove(eq(expectedSessionId))).willReturn(mockSession);
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgument(0);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource())
.isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.<ExpiringSession>getSession()).isSameAs(mockSession);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(isA(SessionDeletedEvent.class));
this.sessionRepository.delete(expectedSessionId);
verify(mockSession, times(1)).getId();
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
@Test
public void deleteRemovesNonExistingSessionAndHandlesDelete() {
final String expectedSessionId = "1";
given(this.mockTemplate.remove(anyString())).willReturn(null);
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgument(0);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource()).
isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.<ExpiringSession>getSession()).isNull();
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(isA(SessionDeletedEvent.class));
this.sessionRepository.delete(expectedSessionId);
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
protected abstract class GemfireOperationsAccessor extends GemfireAccessor
implements GemfireOperations {
}
}

View File

@@ -1,407 +0,0 @@
/*
* Copyright 2014-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 org.springframework.session.data.gemfire.config.annotation.web.http;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* The GemFireHttpSessionConfigurationTest class is a test suite of test cases testing the
* contract and functionality of the {@link GemFireHttpSessionConfiguration} class.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
*/
public class GemFireHttpSessionConfigurationTest {
private GemFireHttpSessionConfiguration gemfireConfiguration;
@SuppressWarnings("unchecked")
protected <T> T getField(Object obj, String fieldName) {
try {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(obj);
}
catch (NoSuchFieldException e) {
throw new IllegalArgumentException(String.format(
"field with name [%1$s] was not found in class [%2$s]", fieldName, obj), e);
}
catch (IllegalAccessException e) {
throw new Error(String.format("unable to access field [%1$s] on object of type [%2$s]",
fieldName, obj.getClass().getName()), e);
}
}
protected <T> T[] toArray(T... array) {
return array;
}
@Before
public void setup() {
this.gemfireConfiguration = new GemFireHttpSessionConfiguration();
}
@Test
public void setAndGetBeanClassLoader() {
assertThat(this.gemfireConfiguration.getBeanClassLoader()).isNull();
this.gemfireConfiguration.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
assertThat(this.gemfireConfiguration.getBeanClassLoader()).isEqualTo(
Thread.currentThread().getContextClassLoader());
this.gemfireConfiguration.setBeanClassLoader(null);
assertThat(this.gemfireConfiguration.getBeanClassLoader()).isNull();
}
@Test
public void setAndGetClientRegionShortcut() {
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(
ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration.setClientRegionShortcut(null);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
}
@Test
public void setAndGetMaxInactiveIntervalInSeconds() {
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(300);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(300);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(Integer.MAX_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(Integer.MAX_VALUE);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(-1);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(-1);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(Integer.MIN_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(Integer.MIN_VALUE);
}
@Test
public void setAndGetPoolName() {
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME);
this.gemfireConfiguration.setPoolName("TestPoolName");
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo("TestPoolName");
this.gemfireConfiguration.setPoolName(" ");
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME);
this.gemfireConfiguration.setPoolName("");
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME);
this.gemfireConfiguration.setPoolName(null);
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_GEMFIRE_POOL_NAME);
}
@Test
public void setAndGetServerRegionShortcut() {
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE_PERSISTENT);
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(
RegionShortcut.REPLICATE_PERSISTENT);
this.gemfireConfiguration.setServerRegionShortcut(null);
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
}
@Test
public void setAndGetSpringSessionGemFireRegionName() {
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("test");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("test");
this.gemfireConfiguration.setSpringSessionGemFireRegionName(" ");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName(null);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
@Test
public void setsImportMetadata() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class);
Map<String, Object> annotationAttributes = new HashMap<String, Object>(4);
annotationAttributes.put("clientRegionShortcut", ClientRegionShortcut.CACHING_PROXY);
annotationAttributes.put("indexableSessionAttributes", toArray("one", "two", "three"));
annotationAttributes.put("maxInactiveIntervalInSeconds", 600);
annotationAttributes.put("poolName", "TestPool");
annotationAttributes.put("serverRegionShortcut", RegionShortcut.REPLICATE);
annotationAttributes.put("regionName", "TEST");
given(mockAnnotationMetadata.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())))
.willReturn(annotationAttributes);
this.gemfireConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(600);
assertThat(this.gemfireConfiguration.getPoolName()).isEqualTo("TestPool");
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("TEST");
verify(mockAnnotationMetadata, times(1)).getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName()));
}
@Test
public void createsAndInitializesSessionRepositoryBean() {
GemfireOperations mockGemfireOperations = mock(GemfireOperations.class);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(120);
GemFireOperationsSessionRepository sessionRepository = this.gemfireConfiguration.sessionRepository(
mockGemfireOperations);
assertThat(sessionRepository).isNotNull();
assertThat(sessionRepository.getTemplate()).isSameAs(mockGemfireOperations);
assertThat(sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(120);
}
@Test
@SuppressWarnings("unchecked")
public void createsAndInitializesSessionRegionTemplateBean() {
GemFireCache mockGemFireCache = mock(GemFireCache.class);
Region<Object, Object> mockRegion = mock(Region.class);
given(mockGemFireCache.getRegion(eq("Example"))).willReturn(mockRegion);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("Example");
GemfireTemplate template = this.gemfireConfiguration.sessionRegionTemplate(mockGemFireCache);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("Example");
assertThat(template).isNotNull();
assertThat(template.getRegion()).isSameAs(mockRegion);
verify(mockGemFireCache, times(1)).getRegion(eq("Example"));
}
@Test
@SuppressWarnings("unchecked")
public void createsAndInitializesSessionRegionBean() {
GemFireCache mockGemFireCache = mock(GemFireCache.class);
RegionAttributes<Object, ExpiringSession> mockRegionAttributes = mock(RegionAttributes.class);
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration.setPoolName("TestPool");
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE_PERSISTENT);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("TestRegion");
GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegionFactoryBean =
this.gemfireConfiguration.sessionRegion(mockGemFireCache, mockRegionAttributes);
assertThat(sessionRegionFactoryBean).isNotNull();
assertThat(this.<ClientRegionShortcut>getField(sessionRegionFactoryBean, "clientRegionShortcut"))
.isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.<GemFireCache>getField(sessionRegionFactoryBean, "gemfireCache"))
.isEqualTo(mockGemFireCache);
assertThat(this.<String>getField(sessionRegionFactoryBean, "poolName")).isEqualTo("TestPool");
assertThat(this.<RegionAttributes<Object, ExpiringSession>>getField(sessionRegionFactoryBean,
"regionAttributes")).isEqualTo(mockRegionAttributes);
assertThat(this.<String>getField(sessionRegionFactoryBean, "regionName")).isEqualTo("TestRegion");
assertThat(this.<RegionShortcut>getField(sessionRegionFactoryBean, "serverRegionShortcut"))
.isEqualTo(RegionShortcut.REPLICATE_PERSISTENT);
verifyZeroInteractions(mockGemFireCache);
verifyZeroInteractions(mockRegionAttributes);
}
@Test
@SuppressWarnings("unchecked")
public void createsAndInitializesSessionRegionAttributesWithExpiration() throws Exception {
Cache mockCache = mock(Cache.class);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(300);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.LOCAL);
RegionAttributesFactoryBean regionAttributesFactory =
this.gemfireConfiguration.sessionRegionAttributes(mockCache);
assertThat(regionAttributesFactory).isNotNull();
regionAttributesFactory.afterPropertiesSet();
RegionAttributes<Object, ExpiringSession> sessionRegionAttributes = regionAttributesFactory.getObject();
assertThat(sessionRegionAttributes).isNotNull();
assertThat(sessionRegionAttributes.getKeyConstraint()).isEqualTo(
GemFireHttpSessionConfiguration.SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT);
assertThat(sessionRegionAttributes.getValueConstraint()).isEqualTo(
GemFireHttpSessionConfiguration.SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT);
ExpirationAttributes entryIdleTimeoutExpiration = sessionRegionAttributes.getEntryIdleTimeout();
assertThat(entryIdleTimeoutExpiration).isNotNull();
assertThat(entryIdleTimeoutExpiration.getAction()).isEqualTo(ExpirationAction.INVALIDATE);
assertThat(entryIdleTimeoutExpiration.getTimeout()).isEqualTo(300);
}
@Test
@SuppressWarnings("unchecked")
public void createsAndInitializesSessionRegionAttributesWithoutExpiration() throws Exception {
ClientCache mockClientCache = mock(ClientCache.class);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(300);
RegionAttributesFactoryBean regionAttributesFactory =
this.gemfireConfiguration.sessionRegionAttributes(mockClientCache);
assertThat(regionAttributesFactory).isNotNull();
regionAttributesFactory.afterPropertiesSet();
RegionAttributes<Object, ExpiringSession> sessionRegionAttributes = regionAttributesFactory.getObject();
assertThat(sessionRegionAttributes).isNotNull();
assertThat(sessionRegionAttributes.getKeyConstraint()).isEqualTo(
GemFireHttpSessionConfiguration.SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT);
assertThat(sessionRegionAttributes.getValueConstraint()).isEqualTo(
GemFireHttpSessionConfiguration.SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT);
ExpirationAttributes entryIdleTimeoutExpiration = sessionRegionAttributes.getEntryIdleTimeout();
assertThat(entryIdleTimeoutExpiration).isNotNull();
assertThat(entryIdleTimeoutExpiration.getAction()).isEqualTo(ExpirationAction.INVALIDATE);
assertThat(entryIdleTimeoutExpiration.getTimeout()).isEqualTo(0);
}
@Test
public void expirationIsAllowed() {
Cache mockCache = mock(Cache.class);
ClientCache mockClientCache = mock(ClientCache.class);
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isTrue();
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isTrue();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_PROXY);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isTrue();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE_PROXY);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isTrue();
}
@Test
public void expirationIsNotAllowed() {
Cache mockCache = mock(Cache.class);
ClientCache mockClientCache = mock(ClientCache.class);
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isFalse();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.LOCAL);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_PROXY);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isFalse();
}
}

View File

@@ -1,295 +0,0 @@
/*
* Copyright 2014-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 org.springframework.session.data.gemfire.config.annotation.web.http.support;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.InterestResultPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.gemfire.client.Interest;
import org.springframework.session.ExpiringSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* The GemFireCacheTypeAwareRegionFactoryBeanTest class is a test suite of test cases
* testing the contract and functionality of the GemFireCacheTypeAwareRegionFactoryBean
* class.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Rule
* @see org.junit.Test
* @see org.junit.rules.ExpectedException
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.InterestResultPolicy
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.RegionAttributes
* @see org.apache.geode.cache.RegionShortcut
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
@RunWith(MockitoJUnitRunner.class)
public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@Mock
Cache mockCache;
@Mock
ClientCache mockClientCache;
@Mock
Region<Object, ExpiringSession> mockClientRegion;
@Mock
Region<Object, ExpiringSession> mockServerRegion;
private GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> regionFactoryBean;
@Before
public void setup() {
this.regionFactoryBean = new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>();
}
protected void afterPropertiesSetCreatesCorrectRegionForGemFireCacheType(final GemFireCache expectedCache,
Region<Object, ExpiringSession> expectedRegion) throws Exception {
this.regionFactoryBean = new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>() {
@Override
protected Region<Object, ExpiringSession> newClientRegion(GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(expectedCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientRegion;
}
@Override
protected Region<Object, ExpiringSession> newServerRegion(GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(expectedCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockServerRegion;
}
};
this.regionFactoryBean.setGemfireCache(expectedCache);
this.regionFactoryBean.afterPropertiesSet();
assertThat(this.regionFactoryBean.getGemfireCache()).isSameAs(expectedCache);
assertThat(this.regionFactoryBean.getObject()).isEqualTo(expectedRegion);
assertThat(this.regionFactoryBean.getObjectType()).isEqualTo(expectedRegion.getClass());
}
@Test
public void afterPropertiesSetCreatesClientRegionForClientCache() throws Exception {
afterPropertiesSetCreatesCorrectRegionForGemFireCacheType(this.mockClientCache, this.mockClientRegion);
}
@Test
public void afterPropertiesSetCreatesServerRegionForPeerCache() throws Exception {
afterPropertiesSetCreatesCorrectRegionForGemFireCacheType(this.mockCache, this.mockServerRegion);
}
@Test
public void allKeysInterestsRegistration() {
Interest<Object>[] interests = this.regionFactoryBean.registerInterests(true);
assertThat(interests).isNotNull();
assertThat(interests.length).isEqualTo(1);
assertThat(interests[0].isDurable()).isFalse();
assertThat(interests[0].getKey().toString()).isEqualTo("ALL_KEYS");
assertThat(interests[0].getPolicy()).isEqualTo(InterestResultPolicy.KEYS);
assertThat(interests[0].isReceiveValues()).isTrue();
}
@Test
public void emptyInterestsRegistration() {
Interest<Object>[] interests = this.regionFactoryBean.registerInterests(false);
assertThat(interests).isNotNull();
assertThat(interests.length).isEqualTo(0);
}
@Test
public void getObjectTypeBeforeInitializationIsRegionClass() {
assertThat(this.regionFactoryBean.getObjectType()).isEqualTo(Region.class);
}
@Test
public void isSingletonIsTrue() {
assertThat(this.regionFactoryBean.isSingleton()).isTrue();
}
@Test
public void setAndGetBeanFactory() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
this.regionFactoryBean.setBeanFactory(mockBeanFactory);
assertThat(this.regionFactoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
}
@Test
public void setBeanFactoryToNullThrowsIllegalArgumentException() {
this.exception.expect(IllegalArgumentException.class);
this.exception.expectMessage("BeanFactory must not be null");
this.regionFactoryBean.setBeanFactory(null);
}
@Test
public void getBeanFactoryWhenNullThrowsIllegalStateException() {
this.exception.expect(IllegalStateException.class);
this.exception.expectMessage("A reference to the BeanFactory was not properly configured");
this.regionFactoryBean.getBeanFactory();
}
@Test
public void setAndGetClientRegionShortcut() {
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
this.regionFactoryBean.setClientRegionShortcut(ClientRegionShortcut.LOCAL_PERSISTENT);
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
ClientRegionShortcut.LOCAL_PERSISTENT);
this.regionFactoryBean.setClientRegionShortcut(null);
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
}
@Test
public void setAndGetGemfireCache() {
Cache mockCache = mock(Cache.class);
this.regionFactoryBean.setGemfireCache(mockCache);
assertThat(this.regionFactoryBean.getGemfireCache()).isEqualTo(mockCache);
}
@Test
public void setGemfireCacheToNullThrowsIllegalArgumentException() {
this.exception.expect(IllegalArgumentException.class);
this.exception.expectMessage("GemFireCache must not be null");
this.regionFactoryBean.setGemfireCache(null);
}
@Test
public void getGemfireCacheWhenNullThrowsIllegalStateException() {
this.exception.expect(IllegalStateException.class);
this.exception.expectMessage("A reference to the GemFireCache was not properly configured");
this.regionFactoryBean.getGemfireCache();
}
@Test
public void setAndGetPoolName() {
assertThat(this.regionFactoryBean.getPoolName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_GEMFIRE_POOL_NAME);
this.regionFactoryBean.setPoolName("TestPoolName");
assertThat(this.regionFactoryBean.getPoolName()).isEqualTo("TestPoolName");
this.regionFactoryBean.setPoolName(" ");
assertThat(this.regionFactoryBean.getPoolName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_GEMFIRE_POOL_NAME);
this.regionFactoryBean.setPoolName("");
assertThat(this.regionFactoryBean.getPoolName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_GEMFIRE_POOL_NAME);
this.regionFactoryBean.setPoolName(null);
assertThat(this.regionFactoryBean.getPoolName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_GEMFIRE_POOL_NAME);
}
@Test
@SuppressWarnings("unchecked")
public void setAndGetRegionAttributes() {
RegionAttributes<Object, ExpiringSession> mockRegionAttributes = mock(RegionAttributes.class);
assertThat(this.regionFactoryBean.getRegionAttributes()).isNull();
this.regionFactoryBean.setRegionAttributes(mockRegionAttributes);
assertThat(this.regionFactoryBean.getRegionAttributes()).isSameAs(mockRegionAttributes);
this.regionFactoryBean.setRegionAttributes(null);
assertThat(this.regionFactoryBean.getRegionAttributes()).isNull();
}
@Test
public void setAndGetRegionName() {
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName("Example");
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo("Example");
this.regionFactoryBean.setRegionName(" ");
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName("");
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName(null);
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
@Test
public void setAndGetServerRegionShortcut() {
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
this.regionFactoryBean.setServerRegionShortcut(RegionShortcut.LOCAL_PERSISTENT);
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(RegionShortcut.LOCAL_PERSISTENT);
this.regionFactoryBean.setServerRegionShortcut(null);
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
}
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.config.annotation.web.http.support;
import org.apache.geode.cache.query.Index;
import org.junit.Before;
import org.junit.Test;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Test suite of test cases testing the contract and functionality
* of the {@link SessionAttributesIndexFactoryBean} class.
*
* @author John Blum
* @since 1.3.0
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.SessionAttributesIndexFactoryBean
*/
public class SessionAttributesIndexFactoryBeanTests {
static <T> T[] toArray(T... array) {
return array;
}
private SessionAttributesIndexFactoryBean indexFactoryBean;
@Before
public void setup() {
this.indexFactoryBean = new SessionAttributesIndexFactoryBean();
}
@Test
public void indexIsNotInitializedWhenNoIndexableSessionAttributesAreConfigured() throws Exception {
final Index mockIndex = mock(Index.class);
SessionAttributesIndexFactoryBean indexFactoryBean = new SessionAttributesIndexFactoryBean() {
@Override
protected Index newIndex() throws Exception {
return mockIndex;
}
};
indexFactoryBean.afterPropertiesSet();
assertThat(indexFactoryBean.getObject()).isNull();
assertThat(indexFactoryBean.getObjectType()).isEqualTo(Index.class);
}
@Test
public void initializesIndexWhenIndexableSessionAttributesAreConfigured() throws Exception {
final Index mockIndex = mock(Index.class);
SessionAttributesIndexFactoryBean indexFactoryBean = new SessionAttributesIndexFactoryBean() {
@Override
protected Index newIndex() throws Exception {
return mockIndex;
}
};
indexFactoryBean.setIndexableSessionAttributes(toArray("one", "two"));
indexFactoryBean.afterPropertiesSet();
assertThat(indexFactoryBean.getObject()).isEqualTo(mockIndex);
assertThat(indexFactoryBean.getObjectType()).isEqualTo(mockIndex.getClass());
}
@Test
public void isSingletonIsTrue() {
assertThat(this.indexFactoryBean.isSingleton()).isTrue();
}
@Test
public void setAndGetIndexableSessionAttributes() {
assertThat(this.indexFactoryBean.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
assertThat(this.indexFactoryBean.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
this.indexFactoryBean.setIndexableSessionAttributes(toArray("one", "two", "three"));
assertThat(this.indexFactoryBean.getIndexableSessionAttributes()).isEqualTo(
toArray("one", "two", "three"));
assertThat(this.indexFactoryBean.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo(
"'one', 'two', 'three'");
this.indexFactoryBean.setIndexableSessionAttributes(toArray("one"));
assertThat(this.indexFactoryBean.getIndexableSessionAttributes()).isEqualTo(toArray("one"));
assertThat(this.indexFactoryBean.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("'one'");
this.indexFactoryBean.setIndexableSessionAttributes(null);
assertThat(this.indexFactoryBean.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
assertThat(this.indexFactoryBean.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
}
@Test
public void setAndGetRegionName() {
assertThat(this.indexFactoryBean.getRegionName()).isNull();
this.indexFactoryBean.setRegionName("Example");
assertThat(this.indexFactoryBean.getRegionName()).isEqualTo("Example");
this.indexFactoryBean.setRegionName(null);
assertThat(this.indexFactoryBean.getRegionName()).isNull();
}
}

View File

@@ -1,203 +0,0 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 org.springframework.session.data.gemfire.support;
import java.io.Closeable;
import java.io.IOException;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* The GemFireUtilsTest class is a test suite of test cases testing the contract and
* functionality of the GemFireUtils utility class.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.support.GemFireUtils
*/
public class GemFireUtilsTest {
@Test
public void closeNonNullCloseableSuccessfullyReturnsTrue() throws IOException {
Closeable mockCloseable = mock(Closeable.class);
assertThat(GemFireUtils.close(mockCloseable)).isTrue();
verify(mockCloseable, times(1)).close();
}
@Test
public void closeNonNullCloseableObjectThrowingIOExceptionReturnsFalse() throws IOException {
Closeable mockCloseable = mock(Closeable.class);
willThrow(new IOException("test")).given(mockCloseable).close();
assertThat(GemFireUtils.close(mockCloseable)).isFalse();
verify(mockCloseable, times(1)).close();
}
@Test
public void closeNullCloseableObjectReturnsFalse() {
assertThat(GemFireUtils.close(null)).isFalse();
}
@Test
public void clientCacheIsClient() {
assertThat(GemFireUtils.isClient(mock(ClientCache.class))).isTrue();
}
@Test
public void genericCacheIsNotClient() {
assertThat(GemFireUtils.isClient(mock(GemFireCache.class))).isFalse();
}
@Test
public void peerCacheIsNotClient() {
assertThat(GemFireUtils.isClient(mock(Cache.class))).isFalse();
}
@Test
public void peerCacheIsPeer() {
assertThat(GemFireUtils.isPeer(mock(Cache.class))).isTrue();
}
@Test
public void genericCacheIsNotPeer() {
assertThat(GemFireUtils.isPeer(mock(GemFireCache.class))).isFalse();
}
@Test
public void clientCacheIsNotPeer() {
assertThat(GemFireUtils.isPeer(mock(ClientCache.class))).isFalse();
}
@Test
public void clientRegionShortcutIsLocal() {
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_HEAP_LRU)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_OVERFLOW)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_PERSISTENT)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isTrue();
}
@Test
public void clientRegionShortcutIsNotLocal() {
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.PROXY)).isFalse();
}
@Test
public void clientRegionShortcutIsProxy() {
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.PROXY)).isTrue();
}
@Test
public void clientRegionShortcutIsNotProxy() {
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isFalse();
}
@Test
public void regionShortcutIsProxy() {
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY)).isTrue();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY_REDUNDANT)).isTrue();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PROXY)).isTrue();
}
@Test
public void regionIsProxy() {
Region mockRegion = mock(Region.class);
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class);
given(mockRegion.getAttributes()).willReturn(mockRegionAttributes);
given(mockRegionAttributes.getDataPolicy()).willReturn(DataPolicy.EMPTY);
assertThat(GemFireUtils.isProxy(mockRegion)).isTrue();
verify(mockRegion, times(1)).getAttributes();
verify(mockRegionAttributes, times(1)).getDataPolicy();
}
@Test
public void regionIsNotProxy() {
Region mockRegion = mock(Region.class);
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class);
given(mockRegion.getAttributes()).willReturn(mockRegionAttributes);
given(mockRegionAttributes.getDataPolicy()).willReturn(DataPolicy.NORMAL);
assertThat(GemFireUtils.isProxy(mockRegion)).isFalse();
verify(mockRegion, times(1)).getAttributes();
verify(mockRegionAttributes, times(1)).getDataPolicy();
}
@Test
public void regionShortcutIsNotProxy() {
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW)).isFalse();
}
@Test
public void toRegionPath() {
assertThat(GemFireUtils.toRegionPath("A")).isEqualTo("/A");
assertThat(GemFireUtils.toRegionPath("Example")).isEqualTo("/Example");
assertThat(GemFireUtils.toRegionPath("/Example")).isEqualTo("//Example");
assertThat(GemFireUtils.toRegionPath("/")).isEqualTo("//");
assertThat(GemFireUtils.toRegionPath("")).isEqualTo("/");
}
}

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<util:properties id="gemfireProperties">
<prop key="name">GemFireHttpSessionXmlConfigurationTests</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">warning</prop>
</util:properties>
<gfe:cache properties-ref="gemfireProperties"/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:indexableSessionAttributes="one, two, three" p:maxInactiveIntervalInSeconds="3600"
p:serverRegionShortcut="#{T(org.apache.geode.cache.RegionShortcut).LOCAL}"
p:springSessionGemFireRegionName="XmlExample"/>
</beans>