Remove GemFire
Fixes gh-768
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
implementation-class=build.GemFireServerPlugin
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[]
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -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[]
|
||||
@@ -1,2 +0,0 @@
|
||||
application.gemfire.client-server.host=localhost
|
||||
application.gemfire.client-server.port=12480
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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("/");
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user