Update to Spring Boot 1.4.1.RELEASE
* Upgrade to Spring Data GemFire 1.8.4.RELEASE * Upgrade to Spring Data MongoDB 1.9.4.RELEASE * Upgrade to Spring Framework 4.3.3.RELEASE (Upgrade to Spring Data Redis 1.7.4.RELEASE failed) Fixes gh-632
This commit is contained in:
@@ -18,7 +18,7 @@ plugins {
|
||||
|
||||
group = 'org.springframework.session'
|
||||
|
||||
ext.springBootVersion = '1.3.2.RELEASE'
|
||||
ext.springBootVersion = '1.4.1.RELEASE'
|
||||
ext.IDE_GRADLE = "$rootDir/gradle/ide.gradle"
|
||||
ext.JAVA_GRADLE = "$rootDir/gradle/java.gradle"
|
||||
ext.SPRING3_GRADLE = "$rootDir/gradle/spring3.gradle"
|
||||
|
||||
@@ -38,8 +38,8 @@ dependencies {
|
||||
"org.springframework.security:spring-security-config:${springSecurityVersion}",
|
||||
"org.springframework.security:spring-security-web:${springSecurityVersion}",
|
||||
"org.springframework.security:spring-security-test:${springSecurityVersion}",
|
||||
'junit:junit:4.11',
|
||||
'org.mockito:mockito-core:1.9.5',
|
||||
"junit:junit:$junitVersion",
|
||||
"org.mockito:mockito-core:$mockitoVersion",
|
||||
"org.springframework:spring-test:$springVersion",
|
||||
"org.assertj:assertj-core:$assertjVersion",
|
||||
"com.hazelcast:hazelcast:$hazelcastVersion",
|
||||
|
||||
@@ -89,16 +89,17 @@ We start with the _Spring Boot_ application for configuring and bootstrapping a
|
||||
include::{samples-dir}httpsession-gemfire-boot/src/main/java/sample/server/GemFireServer.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableGemFireHttpSession` annotation is used on the GemFire Server mainly to define the corresponding
|
||||
<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` allowing us to configure certain aspects of the GemFire Server using
|
||||
<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 setup a Cache Server instance running in the GemFire Server to listen for connections from cache clients.
|
||||
The Cache Server `Socket` will be used to connect our GemFire client cache, _Spring Boot_ web application to the server.
|
||||
<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
|
||||
@@ -106,9 +107,9 @@ configuration to affect GemFire and application configuration/behavior from the
|
||||
|
||||
=== Spring Boot-based GemFire cache client Web application
|
||||
|
||||
Now, we create the _Spring Boot_ web application (a GemFire cache client), exposing our Web service
|
||||
using Spring MVC along with _Spring Session_ backed by GemFire, connected to our _Spring Boot_-based GemFire Server,
|
||||
in order to manage Session state in a cluster/replicated-enabled fashion.
|
||||
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]
|
||||
----
|
||||
@@ -116,28 +117,29 @@ include::{samples-dir}httpsession-gemfire-boot/src/main/java/sample/client/Appli
|
||||
----
|
||||
|
||||
<1> Here, again, we use the `@EnableGemFireHttpSession` annotation to not only configure the GemFire cache client,
|
||||
but also to override the (HTTP) Web application container's `HttpSession` and replace it with a Session implementation
|
||||
backed by _Spring Session_ in conjunction with 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, which will appropriately notify the cache client when the Session times out. Again, we have just resorted
|
||||
to using the default 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> Similary 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 pre-populated
|
||||
connections to the server. Whenever a client Region entry operation corresponding to a Session update occurs,
|
||||
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 is not required by any production code. Specifically, the `BeanPostProcessor` along with the code referenced in *6*
|
||||
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> See <5> above.
|
||||
<7> Navigates the Web application to the home page (`index.html`), which uses **Thymeleaf** templates for server-side
|
||||
<6> Navigates the Web application to the home page (`index.html`), which uses **Thymeleaf** templates for server-side
|
||||
pages.
|
||||
<8> Heartbeat web service endpoint (useful for manual testing purposes).
|
||||
<9> Web service endpoint for adding a Session attributes defined by the user using the Web application UI. In addition,
|
||||
<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".
|
||||
|
||||
@@ -159,11 +161,13 @@ and GemFire out-of-the-box using the following attributes:
|
||||
* `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
|
||||
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
|
||||
|
||||
@@ -83,23 +83,23 @@ Add the following Spring Configuration:
|
||||
include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/webapp/WEB-INF/spring/session-client.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> First, a `Properties` bean is created to reference GemFire configuration common to both the client and server,
|
||||
stored in the `META-INF/spring/application.properties` file.
|
||||
<2> The `application.properties` are used along with the `PropertySourcesPlaceholderConfigurer` bean to replace
|
||||
placeholders in the Spring XML configuration meta-data with property values.
|
||||
<3> Spring annotation configuration support is enabled with `<context:annotation-config/>` element so that any
|
||||
<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.
|
||||
<4> `GemFireHttpSessionConfiguration` is registered to enable Spring Session functionality.
|
||||
<5> Then, a Spring `BeanPostProcessor` is registered to determine whether a GemFire Server at the designated host/port
|
||||
is running, blocking client startup until the server is available.
|
||||
<6> 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 sample application-specific System property, defaulting
|
||||
<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.
|
||||
<7> Finally, we create the GemFire client cache and 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.
|
||||
<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
|
||||
@@ -110,8 +110,8 @@ NOTE: For more information on configuring _Spring Data GemFire_, refer to the ht
|
||||
|
||||
=== Server Configuration
|
||||
|
||||
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
|
||||
session state information up to the server to manage.
|
||||
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:
|
||||
|
||||
@@ -124,15 +124,16 @@ include::{samples-dir}httpsession-gemfire-clientserver-xml/src/main/resources/ME
|
||||
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 from `META-INF/spring/application.properties`.
|
||||
<3> We enable the same Spring Session functionality that 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.
|
||||
<4> Next, we configure the GemFire Server using GemFire System properties very much like our P2P samples.
|
||||
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.
|
||||
<5> Then, we create an instance of the GemFire peer cache using our GemFire System properties.
|
||||
<6> And finally, we also setup a GemFire `CacheServer` instance running on *localhost*, listening on port **11235**,
|
||||
to accept our client connection.
|
||||
<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:
|
||||
|
||||
|
||||
@@ -84,33 +84,26 @@ include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/Clie
|
||||
----
|
||||
|
||||
<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.
|
||||
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> Then, we configure a `Pool` of client connections to talk to the GemFire Server in our Client/Server topology. In our
|
||||
<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].
|
||||
<4> After configuring a `Pool`, we create an instance of the GemFire client cache using the GemFire `Properties`
|
||||
and `Pool` to communicate with the server and perform cache data access operations.
|
||||
<5> Finally, we include a Spring `BeanPostProcessor` to block the client until our GemFire Server is up and running,
|
||||
<56> 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. This `BeanPostProcessor` implements 2 approaches to ensure our server has adequate
|
||||
time to startup.
|
||||
running, such as in production.
|
||||
|
||||
The first approach uses a timed wait, checking at periodic intervals to determine whether a client `Socket` connection
|
||||
can be made to the server's `CacheServer` endpoint.
|
||||
|
||||
The second approach uses a GemFire http://gemfire.docs.pivotal.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/management/membership/ClientMembershipListener.html[ClientMembershipListener]
|
||||
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. Either one of these approaches are sufficient
|
||||
by themselves, but both are demonstrated here to illustrate how this might work and to give you ideas, or other options
|
||||
in practice.
|
||||
`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
|
||||
@@ -122,11 +115,16 @@ NOTE: For more information on configuring _Spring Data GemFire_, refer to the ht
|
||||
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 http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/management_all_region_types/chapter_overview.html[data management policies]
|
||||
* `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`).
|
||||
(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
|
||||
@@ -134,13 +132,13 @@ store Spring Sessions is `LOCAL`, however, keep in mind that your session state
|
||||
and you lose all benefits of using GemFire to store and manage distributed, replicated session state information
|
||||
in a cluster.
|
||||
|
||||
NOTE: `serverRegionShort` is ignored in a client/server cache configuration and only applies when a peer-to-peer (P2P) topology,
|
||||
and more specifically, a GemFire peer cache is used.
|
||||
NOTE: `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
|
||||
|
||||
Now, we have only covered one side of the equation. We also need a GemFire Server for our client to talk to and pass
|
||||
session state up to the server to manage.
|
||||
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:
|
||||
|
||||
@@ -149,15 +147,15 @@ In this sample, we will use the following GemFire Server Java Configuration:
|
||||
include::{samples-dir}httpsession-gemfire-clientserver/src/main/java/sample/ServerConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> On the server, we also configure Spring Session using the `@EnableGemFireHttpSession` annotation. For one, this
|
||||
ensures that the Region names on both the client and server match (in this sample, we use the default "_ClusteredSpringSessions_").
|
||||
<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.
|
||||
<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 the GemFire peer cache using our GemFire System properties.
|
||||
<4> We also setup a GemFire `CacheServer` instance running on **localhost**, listening on port **12480**,
|
||||
to accept our client connection.
|
||||
<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.
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ jspApiVersion=2.0
|
||||
servletApiVersion=3.0.1
|
||||
jstlelVersion=1.2.5
|
||||
version=1.3.0.BUILD-SNAPSHOT
|
||||
# TODO: upgrade to SD Redis 1.7.4.RELEASE - spring3Test suite Redis tests fail due to upgrade...
|
||||
# Caused by: java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter.<init>(Ljava/lang/ClassLoader;)V
|
||||
springDataRedisVersion=1.7.1.RELEASE
|
||||
html5ShivVersion=3.7.3
|
||||
commonsLoggingVersion=1.2
|
||||
@@ -15,13 +17,13 @@ hazelcastVersion=3.6.5
|
||||
springDataGeodeVersion=1.0.0.APACHE-GEODE-INCUBATING-M2
|
||||
seleniumVersion=2.52.0
|
||||
springSecurityVersion=4.2.0.RELEASE
|
||||
springVersion=4.2.5.RELEASE
|
||||
springVersion=4.3.3.RELEASE
|
||||
httpClientVersion=4.5.1
|
||||
jedisVersion=2.8.1
|
||||
h2Version=1.4.192
|
||||
springDataMongoVersion=1.9.1.RELEASE
|
||||
springDataMongoVersion=1.9.4.RELEASE
|
||||
springShellVersion=1.1.0.RELEASE
|
||||
springDataGemFireVersion=1.8.1.RELEASE
|
||||
springDataGemFireVersion=1.8.4.RELEASE
|
||||
assertjVersion=2.5.0
|
||||
spockVersion=1.0-groovy-2.4
|
||||
webjarsTaglibVersion=0.3
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package sample.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
@@ -26,7 +24,6 @@ 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 javax.servlet.http.HttpSession;
|
||||
|
||||
@@ -34,8 +31,6 @@ import com.gemstone.gemfire.cache.client.Pool;
|
||||
import com.gemstone.gemfire.management.membership.ClientMembership;
|
||||
import com.gemstone.gemfire.management.membership.ClientMembershipEvent;
|
||||
import com.gemstone.gemfire.management.membership.ClientMembershipListenerAdapter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -47,8 +42,8 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.PoolFactoryBean;
|
||||
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.support.GemFireUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,16 +54,14 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* A Spring Boot-based GemFire cache client web application that reveals the current state
|
||||
* of the HTTP Session.
|
||||
* 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.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
|
||||
* @see org.springframework.stereotype.Controller
|
||||
* @see com.gemstone.gemfire.cache.client.ClientCache
|
||||
* @since 1.2.1
|
||||
@@ -79,63 +72,42 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@Controller
|
||||
public class Application {
|
||||
|
||||
static final int MAX_CONNECTIONS = 50;
|
||||
|
||||
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
|
||||
static final long DEFAULT_WAIT_INTERVAL = 500L;
|
||||
|
||||
static final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
|
||||
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
|
||||
static final String INDEX_TEMPLATE_VIEW_NAME = "index";
|
||||
static final String PING_RESPONSE = "PONG";
|
||||
static final String REQUEST_COUNT_ATTRIBUTE_NAME = "requestCount";
|
||||
|
||||
static { // <6>
|
||||
ClientMembership
|
||||
.registerClientMembershipListener(new ClientMembershipListenerAdapter() {
|
||||
@Override
|
||||
public void memberJoined(ClientMembershipEvent event) {
|
||||
if (!event.isClient()) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
String applicationName() {
|
||||
return "samples:httpsession-gemfire-boot-"
|
||||
.concat(Application.class.getSimpleName());
|
||||
}
|
||||
|
||||
String gemfireLogLevel() {
|
||||
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
|
||||
return new ConnectionEndpoint(host, port);
|
||||
}
|
||||
|
||||
Properties gemfireProperties() { // <2>
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("log-level", gemfireLogLevel());
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String applicationName() {
|
||||
return "samples:httpsession-gemfire-boot:"
|
||||
.concat(getClass().getSimpleName());
|
||||
}
|
||||
|
||||
String logLevel() {
|
||||
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ClientCacheFactoryBean gemfireCache() { // <3>
|
||||
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
|
||||
@@ -153,18 +125,23 @@ public class Application {
|
||||
|
||||
PoolFactoryBean gemfirePool = new PoolFactoryBean();
|
||||
|
||||
gemfirePool.setMaxConnections(MAX_CONNECTIONS);
|
||||
gemfirePool.setPingInterval(TimeUnit.SECONDS.toMillis(15));
|
||||
gemfirePool.setKeepAlive(false);
|
||||
gemfirePool.setPingInterval(TimeUnit.SECONDS.toMillis(5));
|
||||
gemfirePool.setReadTimeout(Long.valueOf(TimeUnit.SECONDS.toMillis(2)).intValue());
|
||||
gemfirePool.setRetryAttempts(1);
|
||||
gemfirePool.setSubscriptionEnabled(true);
|
||||
gemfirePool.setServerEndpoints(
|
||||
Collections.singleton(newConnectionEndpoint(host, port)));
|
||||
gemfirePool.setThreadLocalConnections(false);
|
||||
gemfirePool.setServers(Collections.singleton(newConnectionEndpoint(host, port)));
|
||||
|
||||
return gemfirePool;
|
||||
}
|
||||
|
||||
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
|
||||
return new ConnectionEndpoint(host, port);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor gemfireCacheServerAvailabilityBeanPostProcessor(
|
||||
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor(
|
||||
@Value("${gemfire.cache.server.host:localhost}") final String host,
|
||||
@Value("${gemfire.cache.server.port:12480}") final int port) { // <5>
|
||||
|
||||
@@ -172,12 +149,15 @@ public class Application {
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
if (!waitForCacheServerToStart(host, port)) {
|
||||
Application.this.logger.warn(
|
||||
"No GemFire Cache Server found on [host: {}, port: {}]",
|
||||
host, port);
|
||||
}
|
||||
|
||||
if ("gemfirePool".equals(beanName)) {
|
||||
ClientMembership.registerClientMembershipListener(
|
||||
new ClientMembershipListenerAdapter() {
|
||||
@Override
|
||||
public void memberJoined(ClientMembershipEvent event) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return bean;
|
||||
@@ -185,13 +165,12 @@ public class Application {
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
|
||||
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
|
||||
try {
|
||||
Assert.state(
|
||||
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
|
||||
String.format(
|
||||
"GemFire Cache Server failed to start on [host: %1$s, port: %2$d]",
|
||||
host, port));
|
||||
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
|
||||
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
|
||||
host, port));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
@@ -204,23 +183,23 @@ public class Application {
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public String index() { // <7>
|
||||
public String index() { // <6>
|
||||
return INDEX_TEMPLATE_VIEW_NAME;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/ping")
|
||||
@ResponseBody
|
||||
public String ping() { // <8>
|
||||
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) { // <9>
|
||||
@RequestParam(name = "attributeValue", required = false) String value) { // <8>
|
||||
|
||||
modelMap.addAttribute("sessionAttributes",
|
||||
attributes(setAttribute(updateRequestCount(session), name, value)));
|
||||
attributes(setAttribute(updateRequestCount(session), name, value)));
|
||||
|
||||
return INDEX_TEMPLATE_VIEW_NAME;
|
||||
}
|
||||
@@ -230,27 +209,24 @@ public class Application {
|
||||
@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));
|
||||
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 (nullSafeInt(value) + 1);
|
||||
return (nullSafeIntValue(value) + 1);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
int nullSafeInt(Number value) {
|
||||
int nullSafeIntValue(Number value) {
|
||||
return (value != null ? value.intValue() : 0);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
HttpSession setAttribute(HttpSession session, String attributeName,
|
||||
String attributeValue) {
|
||||
HttpSession setAttribute(HttpSession session, String attributeName, String attributeValue) {
|
||||
if (isSet(attributeName, attributeValue)) {
|
||||
session.setAttribute(attributeName, attributeValue);
|
||||
}
|
||||
@@ -269,97 +245,25 @@ public class Application {
|
||||
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)));
|
||||
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()
|
||||
: new Iterator<T>() {
|
||||
public boolean hasNext() {
|
||||
return enumeration.hasMoreElements();
|
||||
}
|
||||
|
||||
public T next() {
|
||||
return enumeration.nextElement();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Auto-generated method stub");
|
||||
}
|
||||
});
|
||||
: CollectionUtils.toIterator(enumeration));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
boolean waitForCacheServerToStart(String host, int port) {
|
||||
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
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 {
|
||||
// NOTE: this code is not intended to be an atomic, compound action (a
|
||||
// possible race condition);
|
||||
// opening another connection (at the expense of using system
|
||||
// resources) after connectivity
|
||||
// has already been established is not detrimental in this use case
|
||||
if (!this.connected.get()) {
|
||||
socket = new Socket(host, port);
|
||||
this.connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
GemFireUtils.close(socket);
|
||||
}
|
||||
|
||||
return this.connected.get();
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
boolean waitOnCondition(Condition condition) {
|
||||
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
boolean waitOnCondition(Condition condition, long duration) {
|
||||
final 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();
|
||||
}
|
||||
|
||||
interface Condition {
|
||||
boolean evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl
|
||||
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 20) // <1>
|
||||
public class GemFireServer {
|
||||
|
||||
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
|
||||
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication springApplication = new SpringApplication(GemFireServer.class);
|
||||
@@ -60,25 +60,27 @@ public class GemFireServer {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
String applicationName() {
|
||||
return "samples:httpsession-gemfire-boot-"
|
||||
.concat(GemFireServer.class.getSimpleName());
|
||||
}
|
||||
|
||||
String gemfireLogLevel() {
|
||||
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
Properties gemfireProperties() { // <2>
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level", gemfireLogLevel());
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
gemfireProperties.setProperty("jmx-manager", "true");
|
||||
gemfireProperties.setProperty("jmx-manager-start", "true");
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String applicationName() {
|
||||
return "samples:httpsession-gemfire-boot:"
|
||||
.concat(getClass().getSimpleName());
|
||||
}
|
||||
|
||||
String logLevel() {
|
||||
return System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() { // <3>
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
@@ -98,12 +100,11 @@ public class GemFireServer {
|
||||
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
|
||||
|
||||
gemfireCacheServer.setAutoStartup(true);
|
||||
gemfireCacheServer.setCache(gemfireCache);
|
||||
gemfireCacheServer.setBindAddress(bindAddress);
|
||||
gemfireCacheServer.setCache(gemfireCache);
|
||||
gemfireCacheServer.setHostNameForClients(hostnameForClients);
|
||||
gemfireCacheServer.setMaxTimeBetweenPings(
|
||||
Long.valueOf(TimeUnit.MINUTES.toMillis(1)).intValue());
|
||||
gemfireCacheServer.setNotifyBySubscription(true);
|
||||
Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
|
||||
gemfireCacheServer.setPort(port);
|
||||
|
||||
return gemfireCacheServer;
|
||||
|
||||
@@ -17,6 +17,8 @@ dependencies {
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
runtime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
|
||||
|
||||
testCompile "junit:junit:$junitVersion"
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
|
||||
@@ -27,9 +27,8 @@ import org.springframework.context.annotation.ImportResource;
|
||||
public class Application {
|
||||
|
||||
public static void main(final String[] args) {
|
||||
AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(Application.class);
|
||||
context.registerShutdownHook();
|
||||
new AnnotationConfigApplicationContext(Application.class)
|
||||
.registerShutdownHook();
|
||||
}
|
||||
}
|
||||
// tag::end[]
|
||||
|
||||
@@ -16,14 +16,8 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import com.gemstone.gemfire.cache.client.Pool;
|
||||
import com.gemstone.gemfire.management.membership.ClientMembership;
|
||||
@@ -33,53 +27,42 @@ import com.gemstone.gemfire.management.membership.ClientMembershipListenerAdapte
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.data.gemfire.client.PoolFactoryBean;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
|
||||
static final long DEFAULT_WAIT_INTERVAL = 500L;
|
||||
|
||||
static final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
static final String DEFAULT_SERVER_HOST = "localhost";
|
||||
|
||||
@Value("${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}")
|
||||
int port;
|
||||
|
||||
// tag::class[]
|
||||
static {
|
||||
ClientMembership.registerClientMembershipListener(
|
||||
new ClientMembershipListenerAdapter() {
|
||||
public void memberJoined(final ClientMembershipEvent event) {
|
||||
if (!event.isClient()) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@Resource(name = "applicationProperties")
|
||||
private Properties applicationProperties;
|
||||
@Value("${application.gemfire.client-server.host:localhost}")
|
||||
String host;
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
String host = getServerHost(DEFAULT_SERVER_HOST);
|
||||
Assert.isTrue(waitForCacheServerToStart(host, this.port),
|
||||
String.format("GemFire Server failed to start [host: '%1$s', port: %2$d]%n",
|
||||
host, this.port));
|
||||
if ("gemfirePool".equals(beanName)) {
|
||||
ClientMembership.registerClientMembershipListener(
|
||||
new ClientMembershipListenerAdapter() {
|
||||
@Override
|
||||
public void memberJoined(ClientMembershipEvent event) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
|
||||
try {
|
||||
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
|
||||
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
|
||||
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
|
||||
this.host, this.port));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
@@ -89,70 +72,4 @@ public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcess
|
||||
return bean;
|
||||
}
|
||||
// tag::end[]
|
||||
|
||||
interface Condition {
|
||||
boolean evaluate();
|
||||
}
|
||||
|
||||
String getServerHost(String defaultServerHost) {
|
||||
return this.applicationProperties
|
||||
.getProperty("application.gemfire.client-server.host", defaultServerHost);
|
||||
}
|
||||
|
||||
boolean waitForCacheServerToStart(String host, int port) {
|
||||
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
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 {
|
||||
// NOTE: this code is not intended to be an atomic, compound action (a
|
||||
// possible race condition);
|
||||
// opening another connection (at the expense of using system
|
||||
// resources) after connectivity
|
||||
// has already been established is not detrimental in this use case
|
||||
if (!connected.get()) {
|
||||
socket = new Socket(host, port);
|
||||
connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
GemFireUtils.close(socket);
|
||||
}
|
||||
|
||||
return connected.get();
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
boolean waitOnCondition(Condition condition) {
|
||||
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
boolean waitOnCondition(Condition condition, long duration) {
|
||||
final 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
application.gemfire.client-server.host=localhost
|
||||
application.gemfire.client-server.port=11235
|
||||
application.gemfire.client-server.max-connections=50
|
||||
application.gemfire.client-server.port=12480
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
|
||||
|
||||
<!--3-->
|
||||
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
|
||||
p:maxInactiveIntervalInSeconds="30"/>
|
||||
|
||||
<!--4-->
|
||||
<util:properties id="gemfireProperties">
|
||||
<prop key="name">GemFireClientServerHttpSessionXmlSample</prop>
|
||||
<prop key="mcast-port">0</prop>
|
||||
@@ -32,15 +28,18 @@
|
||||
<prop key="jmx-manager-start">true</prop>
|
||||
</util:properties>
|
||||
|
||||
<!--5-->
|
||||
<gfe:cache properties-ref="gemfireProperties"
|
||||
use-bean-factory-locator="false"/>
|
||||
<!--4-->
|
||||
<gfe:cache properties-ref="gemfireProperties"/>
|
||||
|
||||
<!--6-->
|
||||
<!--5-->
|
||||
<gfe:cache-server auto-startup="true"
|
||||
bind-address="${application.gemfire.client-server.host}"
|
||||
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"
|
||||
max-connections="${application.gemfire.client-server.max-connections}"/>
|
||||
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>
|
||||
|
||||
@@ -14,41 +14,36 @@
|
||||
|
||||
<!-- tag::beans[] -->
|
||||
<!--1-->
|
||||
<util:properties id="applicationProperties"
|
||||
location="classpath:META-INF/spring/application.properties"/>
|
||||
|
||||
<!--2-->
|
||||
<context:property-placeholder properties-ref="applicationProperties"/>
|
||||
|
||||
<!--3-->
|
||||
<context:annotation-config/>
|
||||
|
||||
<!--4-->
|
||||
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
|
||||
p:maxInactiveIntervalInSeconds="30"/>
|
||||
<!--2-->
|
||||
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
|
||||
|
||||
<!--5-->
|
||||
<!--3-->
|
||||
<bean class="sample.GemFireCacheServerReadyBeanPostProcessor"/>
|
||||
|
||||
<!--6-->
|
||||
<!--4-->
|
||||
<util:properties id="gemfireProperties">
|
||||
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
|
||||
</util:properties>
|
||||
|
||||
<!--5-->
|
||||
<gfe:client-cache properties-ref="gemfireProperties"/>
|
||||
|
||||
<!--7-->
|
||||
<gfe:pool free-connection-timeout="5000"
|
||||
keep-alive="false"
|
||||
<!--6-->
|
||||
<gfe:pool keep-alive="false"
|
||||
ping-interval="5000"
|
||||
read-timeout="5000"
|
||||
retry-attempts="2"
|
||||
retry-attempts="1"
|
||||
subscription-enabled="true"
|
||||
thread-local-connections="false"
|
||||
max-connections="${application.gemfire.client-server.max-connections}">
|
||||
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"/>
|
||||
<!-- end::beans[] -->
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -12,6 +12,8 @@ dependencies {
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
runtime "org.springframework.shell:spring-shell:1.0.0.RELEASE"
|
||||
|
||||
testCompile "junit:junit:$junitVersion"
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
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 com.gemstone.gemfire.cache.client.Pool;
|
||||
import com.gemstone.gemfire.management.membership.ClientMembership;
|
||||
@@ -38,7 +35,6 @@ import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.PoolFactoryBean;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
|
||||
import org.springframework.session.data.gemfire.support.GemFireUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
// tag::class[]
|
||||
@@ -46,39 +42,35 @@ import org.springframework.util.Assert;
|
||||
public class ClientConfig {
|
||||
|
||||
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
|
||||
static final long DEFAULT_WAIT_INTERVAL = 500L;
|
||||
|
||||
static final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
static {
|
||||
System.setProperty("gemfire.log-level", logLevel());
|
||||
|
||||
ClientMembership.registerClientMembershipListener(
|
||||
new ClientMembershipListenerAdapter() {
|
||||
public void memberJoined(ClientMembershipEvent event) {
|
||||
if (!event.isClient()) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String logLevel() {
|
||||
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
|
||||
}
|
||||
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() { // <2>
|
||||
return new Properties();
|
||||
Properties gemfireProperties = new Properties();
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
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
|
||||
ClientCacheFactoryBean gemfireCache() { // <4>
|
||||
ClientCacheFactoryBean gemfireCache() { // <3>
|
||||
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
|
||||
|
||||
clientCacheFactory.setClose(true);
|
||||
@@ -88,46 +80,56 @@ public class ClientConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
PoolFactoryBean gemfirePool(// <3>
|
||||
PoolFactoryBean gemfirePool(// <4>
|
||||
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) {
|
||||
|
||||
PoolFactoryBean poolFactory = new PoolFactoryBean();
|
||||
|
||||
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
|
||||
poolFactory.setKeepAlive(false);
|
||||
poolFactory.setMaxConnections(ServerConfig.MAX_CONNECTIONS);
|
||||
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
|
||||
poolFactory.setReadTimeout(2000); // 2 seconds
|
||||
poolFactory.setRetryAttempts(2);
|
||||
poolFactory.setRetryAttempts(1);
|
||||
poolFactory.setSubscriptionEnabled(true);
|
||||
poolFactory.setThreadLocalConnections(false);
|
||||
|
||||
poolFactory.setServers(Collections.singletonList(
|
||||
new ConnectionEndpoint(ServerConfig.SERVER_HOSTNAME, port)));
|
||||
newConnectionEndpoint(ServerConfig.SERVER_HOST, port)));
|
||||
|
||||
return poolFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor(// <5>
|
||||
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") final int port) {
|
||||
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
|
||||
return new ConnectionEndpoint(host, port);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor() { // <5>
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
Assert.isTrue(waitForCacheServerToStart(ServerConfig.SERVER_HOSTNAME, port),
|
||||
String.format("GemFire Server failed to start [hostname: %1$s, port: %2$d]",
|
||||
ServerConfig.SERVER_HOSTNAME, port));
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
if ("gemfirePool".equals(beanName)) {
|
||||
ClientMembership.registerClientMembershipListener(
|
||||
new ClientMembershipListenerAdapter() {
|
||||
@Override
|
||||
public void memberJoined(ClientMembershipEvent event) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
|
||||
try {
|
||||
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
|
||||
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
|
||||
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
|
||||
ServerConfig.SERVER_HOST, ServerConfig.SERVER_PORT));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
@@ -139,65 +141,4 @@ public class ClientConfig {
|
||||
};
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
interface Condition {
|
||||
boolean evaluate();
|
||||
}
|
||||
|
||||
boolean waitForCacheServerToStart(String host, int port) {
|
||||
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
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 {
|
||||
// NOTE: this code is not intended to be an atomic, compound action (a
|
||||
// possible race condition);
|
||||
// opening another connection (at the expense of using system
|
||||
// resources) after connectivity
|
||||
// has already been established is not detrimental in this use case
|
||||
if (!connected.get()) {
|
||||
socket = new Socket(host, port);
|
||||
connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
GemFireUtils.close(socket);
|
||||
}
|
||||
|
||||
return connected.get();
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
boolean waitOnCondition(Condition condition) {
|
||||
return waitOnCondition(condition, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
boolean waitOnCondition(Condition condition, long duration) {
|
||||
final 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
|
||||
@@ -33,13 +34,19 @@ import org.springframework.session.data.gemfire.config.annotation.web.http.Enabl
|
||||
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) // <1>
|
||||
public class ServerConfig {
|
||||
|
||||
static final int MAX_CONNECTIONS = 50;
|
||||
static final int SERVER_PORT = 12480;
|
||||
|
||||
static final String SERVER_HOSTNAME = "localhost";
|
||||
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
|
||||
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ public class ServerConfig {
|
||||
Properties gemfireProperties() { // <2>
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", "GemFireClientServerHttpSessionSample");
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
gemfireProperties.setProperty("jmx-manager", "true");
|
||||
@@ -56,8 +63,14 @@ public class ServerConfig {
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
private String logLevel() {
|
||||
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
|
||||
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
|
||||
@@ -71,25 +84,19 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache, // <4>
|
||||
@Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) {
|
||||
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
|
||||
@Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) { // <4>
|
||||
|
||||
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
|
||||
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
|
||||
|
||||
cacheServerFactory.setAutoStartup(true);
|
||||
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
|
||||
cacheServerFactory.setCache(gemfireCache);
|
||||
cacheServerFactory.setHostNameForClients(SERVER_HOSTNAME);
|
||||
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
|
||||
cacheServerFactory.setPort(port);
|
||||
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 cacheServerFactory;
|
||||
return gemfireCacheServer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static void main(final String[] args) throws IOException { // <5>
|
||||
new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook();
|
||||
}
|
||||
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -42,9 +42,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The GemFireHttpSessionJavaConfigurationTests class is a test suite of test cases
|
||||
* testing the configuration of Spring Session backed by GemFire using Java-based
|
||||
* configuration meta-data.
|
||||
* Test suite of test cases testing the configuration of Spring Session backed by GemFire
|
||||
* using Java-based configuration meta-data.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.1.0
|
||||
@@ -62,14 +61,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ContextConfiguration
|
||||
@DirtiesContext
|
||||
@WebAppConfiguration
|
||||
public class GemFireHttpSessionJavaConfigurationTests
|
||||
extends AbstractGemFireIntegrationTests {
|
||||
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);
|
||||
@@ -81,16 +80,16 @@ public class GemFireHttpSessionJavaConfigurationTests
|
||||
|
||||
@Test
|
||||
public void gemfireCacheConfigurationIsValid() {
|
||||
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
|
||||
"JavaExample", DataPolicy.REPLICATE);
|
||||
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);
|
||||
Region<Object, ExpiringSession> example =
|
||||
assertCacheAndRegion(this.gemfireCache, "JavaExample", DataPolicy.REPLICATE);
|
||||
|
||||
QueryService queryService = example.getRegionService().getQueryService();
|
||||
|
||||
@@ -103,32 +102,38 @@ public class GemFireHttpSessionJavaConfigurationTests
|
||||
|
||||
@Test
|
||||
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasNotCreated() {
|
||||
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
|
||||
"JavaExample", DataPolicy.REPLICATE);
|
||||
Region<Object, ExpiringSession> example =
|
||||
assertCacheAndRegion(this.gemfireCache, "JavaExample", DataPolicy.REPLICATE);
|
||||
|
||||
QueryService queryService = example.getRegionService().getQueryService();
|
||||
|
||||
assertThat(queryService).isNotNull();
|
||||
|
||||
Index sessionAttributesIndex = queryService.getIndex(example,
|
||||
"sessionAttributesIndex");
|
||||
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
|
||||
|
||||
assertThat(sessionAttributesIndex).isNull();
|
||||
}
|
||||
|
||||
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900, regionName = "JavaExample", serverRegionShortcut = RegionShortcut.REPLICATE)
|
||||
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900,
|
||||
regionName = "JavaExample", serverRegionShortcut = RegionShortcut.REPLICATE)
|
||||
public static class GemFireConfiguration {
|
||||
|
||||
@Bean
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
gemfireProperties.setProperty("name",
|
||||
GemFireHttpSessionJavaConfigurationTests.class.getName());
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level", "warning");
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
String applicationName() {
|
||||
return GemFireHttpSessionJavaConfigurationTests.class.getName();
|
||||
}
|
||||
|
||||
String logLevel() {
|
||||
return "warning";
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean cacheFactory = new CacheFactoryBean();
|
||||
@@ -139,5 +144,4 @@ public class GemFireHttpSessionJavaConfigurationTests
|
||||
return cacheFactory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,9 +37,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The GemFireHttpSessionXmlConfigurationTests class is a test suite of test cases testing
|
||||
* the configuration of Spring Session backed by GemFire using XML configuration
|
||||
* meta-data.
|
||||
* 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
|
||||
@@ -57,14 +56,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ContextConfiguration
|
||||
@DirtiesContext
|
||||
@WebAppConfiguration
|
||||
public class GemFireHttpSessionXmlConfigurationTests
|
||||
extends AbstractGemFireIntegrationTests {
|
||||
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);
|
||||
@@ -76,16 +75,16 @@ public class GemFireHttpSessionXmlConfigurationTests
|
||||
|
||||
@Test
|
||||
public void gemfireCacheConfigurationIsValid() {
|
||||
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
|
||||
"XmlExample", DataPolicy.NORMAL);
|
||||
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);
|
||||
Region<Object, ExpiringSession> example =
|
||||
assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
|
||||
|
||||
QueryService queryService = example.getRegionService().getQueryService();
|
||||
|
||||
@@ -98,18 +97,16 @@ public class GemFireHttpSessionXmlConfigurationTests
|
||||
|
||||
@Test
|
||||
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasCreatedSuccessfully() {
|
||||
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
|
||||
"XmlExample", DataPolicy.NORMAL);
|
||||
Region<Object, ExpiringSession> example =
|
||||
assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
|
||||
|
||||
QueryService queryService = example.getRegionService().getQueryService();
|
||||
|
||||
assertThat(queryService).isNotNull();
|
||||
|
||||
Index sessionAttributesIndex = queryService.getIndex(example,
|
||||
"sessionAttributesIndex");
|
||||
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
|
||||
|
||||
assertIndex(sessionAttributesIndex, "s.attributes['one', 'two', 'three']",
|
||||
String.format("%1$s s", example.getFullPath()));
|
||||
String.format("%1$s s", example.getFullPath()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ import org.springframework.session.config.annotation.web.http.SpringHttpSessionC
|
||||
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.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,15 @@ import org.springframework.util.StringUtils;
|
||||
* HttpSession provider implementation in Spring Session.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see EnableGemFireHttpSession
|
||||
* @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.GemfireTemplate
|
||||
* @see org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration
|
||||
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
|
||||
* @see com.gemstone.gemfire.cache.GemFireCache
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -97,7 +105,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
|
||||
/**
|
||||
* The default names of all Session attributes that should be indexed by GemFire.
|
||||
*/
|
||||
public static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = new String[0];
|
||||
public static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = {};
|
||||
|
||||
private int maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
|
||||
|
||||
@@ -185,28 +193,6 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
|
||||
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all Session attributes that will be indexed by GemFire as single
|
||||
* String value constituting the Index expression of the Index definition.
|
||||
*
|
||||
* @return a String composed of all the named Session attributes on which a GemFire
|
||||
* Index will be created as an Index definition expression. If the indexable Session
|
||||
* attributes were not specified, then the wildcard ("*") is returned.
|
||||
* @see com.gemstone.gemfire.cache.query.Index#getIndexedExpression()
|
||||
*/
|
||||
protected String getIndexableSessionAttributesAsGemFireIndexExpression() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (String sessionAttribute : getIndexableSessionAttributes()) {
|
||||
builder.append(builder.length() > 0 ? ", " : "");
|
||||
builder.append(String.format("'%1$s'", sessionAttribute));
|
||||
}
|
||||
|
||||
String indexExpression = builder.toString();
|
||||
|
||||
return (indexExpression.isEmpty() ? "*" : indexExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum interval in seconds in which a Session can remain inactive before
|
||||
* it is considered expired.
|
||||
@@ -385,7 +371,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
|
||||
*/
|
||||
@Bean(name = DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
|
||||
public GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion(GemFireCache gemfireCache,
|
||||
RegionAttributes<Object, ExpiringSession> sessionRegionAttributes) {
|
||||
@Qualifier("sessionRegionAttributes") RegionAttributes<Object, ExpiringSession> sessionRegionAttributes) {
|
||||
|
||||
GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion =
|
||||
new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>();
|
||||
@@ -448,27 +434,19 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and
|
||||
* managing Sessions, specifically on the 'principalName' property for quick lookup
|
||||
* and queries. This index will only be created on a server @{link Region}.
|
||||
* 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 IndexFactoryBean creating an GemFire Index on the 'principalName'
|
||||
* property of Sessions stored in the GemFire cache {@link Region}.
|
||||
* @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 com.gemstone.gemfire.cache.GemFireCache
|
||||
*/
|
||||
@Bean
|
||||
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
|
||||
public IndexFactoryBean principalNameIndex(final GemFireCache gemfireCache) {
|
||||
IndexFactoryBean index = new IndexFactoryBean() {
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (GemFireUtils.isPeer(gemfireCache)) {
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
};
|
||||
public IndexFactoryBean principalNameIndex(GemFireCache gemfireCache) {
|
||||
IndexFactoryBean index = new IndexFactoryBean();
|
||||
|
||||
index.setCache(gemfireCache);
|
||||
index.setName("principalNameIndex");
|
||||
@@ -481,37 +459,25 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and
|
||||
* managing Sessions, specifically on Session attributes for quick lookup and queries
|
||||
* on Session attribute names with a given value. This index will only be created on a
|
||||
* server @{link Region}.
|
||||
* 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 IndexFactoryBean creating an GemFire Index on attributes of Sessions
|
||||
* @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 com.gemstone.gemfire.cache.GemFireCache
|
||||
*/
|
||||
@Bean
|
||||
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
|
||||
public IndexFactoryBean sessionAttributesIndex(final GemFireCache gemfireCache) {
|
||||
IndexFactoryBean index = new IndexFactoryBean() {
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (GemFireUtils.isPeer(gemfireCache) && !ObjectUtils.isEmpty(getIndexableSessionAttributes())) {
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
};
|
||||
public SessionAttributesIndexFactoryBean sessionAttributesIndex(GemFireCache gemfireCache) {
|
||||
SessionAttributesIndexFactoryBean sessionAttributesIndex = new SessionAttributesIndexFactoryBean();
|
||||
|
||||
index.setCache(gemfireCache);
|
||||
index.setName("sessionAttributesIndex");
|
||||
index.setExpression(String.format("s.attributes[%1$s]",
|
||||
getIndexableSessionAttributesAsGemFireIndexExpression()));
|
||||
index.setFrom(String.format("%1$s s", GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName())));
|
||||
index.setOverride(true);
|
||||
sessionAttributesIndex.setGemFireCache(gemfireCache);
|
||||
sessionAttributesIndex.setIndexableSessionAttributes(getIndexableSessionAttributes());
|
||||
sessionAttributesIndex.setRegionName(getSpringSessionGemFireRegionName());
|
||||
|
||||
return index;
|
||||
return sessionAttributesIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 com.gemstone.gemfire.cache.GemFireCache;
|
||||
import com.gemstone.gemfire.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 com.gemstone.gemfire.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 com.gemstone.gemfire.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;
|
||||
}
|
||||
}
|
||||
@@ -127,34 +127,6 @@ public class GemFireHttpSessionConfigurationTest {
|
||||
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetIndexableSessionAttributes() {
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
|
||||
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
|
||||
|
||||
this.gemfireConfiguration.setIndexableSessionAttributes(toArray("one", "two", "three"));
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
|
||||
toArray("one", "two", "three"));
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo(
|
||||
"'one', 'two', 'three'");
|
||||
|
||||
this.gemfireConfiguration.setIndexableSessionAttributes(toArray("one"));
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one"));
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("'one'");
|
||||
|
||||
this.gemfireConfiguration.setIndexableSessionAttributes(null);
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
|
||||
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
|
||||
|
||||
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetMaxInactiveIntervalInSeconds() {
|
||||
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(
|
||||
@@ -363,6 +335,7 @@ public class GemFireHttpSessionConfigurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createsAndInitializesSessionRegionAttributesWithoutExpiration() throws Exception {
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
@@ -431,5 +404,4 @@ public class GemFireHttpSessionConfigurationTest {
|
||||
|
||||
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 com.gemstone.gemfire.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();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<prop key="log-level">warning</prop>
|
||||
</util:properties>
|
||||
|
||||
<gfe:cache properties-ref="gemfireProperties" close="true"/>
|
||||
<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"
|
||||
|
||||
Reference in New Issue
Block a user