Add generic GemFire findByIndexNameAndIndexValue support

Fixes gh-353
This commit is contained in:
John Blum
2016-02-03 23:04:20 -08:00
committed by Rob Winch
parent 7de11753a9
commit c2b407189e
13 changed files with 611 additions and 50 deletions

View File

@@ -257,6 +257,56 @@ Guide when integrating with your own application.
include::guides/httpsession-gemfire-p2p-xml.adoc[tags=config,leveloffset=+3]
[[httpsessoin-gemfire-indexing]]
==== Using Indexes with GemFire
While best practices concerning the proper definition of indexes that positively impact GemFire's performance is beyond
the scope of this document, it is important to realize that Spring Session Data GemFire creates and uses indexes to
query and find Sessions efficiently.
Out-of-the-box, Spring Session Data GemFire creates 1 Hash-typed Index on the implementing Session's `principalName`
property, which corresponds to the Session attribute...
----
org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME
----
This enables developers using the `GemFireOperationsSessionRepository` programmatically to query and find all Sessions
with a given principal name efficiently.
Additionally, Spring Session Data GemFire will create a Range-based Index on the implementing Session's Map-type
`attributes` property (i.e. on any arbitrary Session attribute) when a developer identifies 1 or more named Session
attributes that should be indexed by GemFire.
Sessions attributes to index can be specified with the `indexableSessionAttributes` attribute on the `@EnableGemFireHttpSession`
annotation. A developer adds this annotation to their Spring application `@Configuration` class when s/he wishes to
enable Spring Session support for HttpSession backed by GemFire.
For example:
.ApplicationConfiguration.java
----
@EnableGemFireHttpSession(indexableSessionAttributes = { "name1", "name2", "name3" })
class ApplicationConfiguration {
// @Bean definition methods implemented here
}
----
NOTE: Only Session attribute names identified in the `@EnableGemFireHttpSession` annotation's `indexableSessionAttributes`
attribute will have an Index defined. All other Session attributes will not be indexed.
However, there is one caveat. Any values stored in indexable Session attributes must implement the `java.lang.Comparable<T>`
interface. If those object values do not implement `Comparable`, then GemFire will throw an error on startup when the
Index is defined for Regions with persistent Session data, or when an attempt is made at runtime to assign the indexable
Session attribute a value that is not `Comparable` and the Session is saved to GemFire.
NOTE: Any Session attribute that is not indexed may store non-`Comparable` values.
To learn more about GemFire's Range-based Indexes, see http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/creating_map_indexes.html[Creating Indexes on Map Fields].
To learn more about GemFire Indexing in general, see http://gemfire.docs.pivotal.io/docs-gemfire/latest/developing/query_index/query_index.html[Working with Indexes].
[[httpsession-how]]
=== How HttpSession Integration Works

View File

@@ -43,6 +43,7 @@ import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.ClientCacheFactory;
import com.gemstone.gemfire.cache.query.Index;
import com.gemstone.gemfire.cache.server.CacheServer;
/**
@@ -105,7 +106,7 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static List<String> createJavaProcessCommandLine(Class<? extends Object> type, String... args) {
protected static List<String> createJavaProcessCommandLine(Class<?> type, String... args) {
List<String> commandLine = new ArrayList<String>();
String javaHome = System.getProperty("java.home");
@@ -155,7 +156,7 @@ public class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static Process run(Class<? extends Object> type, File directory, String... args) throws IOException {
protected static Process run(Class<?> type, File directory, String... args) throws IOException {
return new ProcessBuilder()
.command(createJavaProcessCommandLine(type, args))
.directory(directory)
@@ -318,6 +319,13 @@ public class AbstractGemFireIntegrationTests {
assertThat(actualRegion.getAttributes().getDataPolicy()).isEqualTo(expectedDataPolicy);
}
/* (non-Javadoc) */
protected void assertIndex(Index index, String expectedExpression, String expectedFromClause) {
assertThat(index).isNotNull();
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
}
/* (non-Javadoc) */
protected void assertEntryIdleTimeout(Region<?, ?> region, ExpirationAction expectedAction, int expectedTimeout) {
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(), expectedAction, expectedTimeout);

View File

@@ -17,6 +17,7 @@
package org.springframework.session.data.gemfire;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
import static org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.GemFireSession;
import java.util.Arrays;
@@ -72,11 +73,11 @@ import com.gemstone.gemfire.pdx.PdxWriter;
@ContextConfiguration
@WebAppConfiguration
public class GemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 300;
private static final String GEMFIRE_LOG_LEVEL = "warning";
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestPartitionedSessions";
SecurityContext context;
@@ -102,11 +103,15 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
protected Map<String, ExpiringSession> doFindByPrincipalName(String principalName) {
return gemfireSessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
protected Map<String, ExpiringSession> doFindByIndexNameAndIndexValue(String indexName, String indexValue) {
return gemfireSessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
}
@SuppressWarnings("unchecked")
protected Map<String, ExpiringSession> doFindByPrincipalName(String principalName) {
return doFindByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);
}
@SuppressWarnings({ "unused", "unchecked" })
protected Map<String, ExpiringSession> doFindByPrincipalName(String regionName, String principalName) {
try {
Region<String, ExpiringSession> region = gemfireCache.getRegion(regionName);
@@ -115,7 +120,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
QueryService queryService = region.getRegionService().getQueryService();
String queryString = String.format("SELECT s FROM %1$s s WHERE s.principalName = $1", region.getFullPath());
String queryString = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
region.getFullPath());
Query query = queryService.newQuery(queryString);
@@ -140,6 +146,56 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
return true;
}
protected ExpiringSession setAttribute(ExpiringSession session, String attributeName, Object attributeValue) {
session.setAttribute(attributeName, attributeValue);
return session;
}
@Test
public void findSessionsByIndexedSessionAttributeNameValues() {
ExpiringSession johnBlumSession = save(touch(setAttribute(createSession("johnBlum"), "vip", "yes")));
ExpiringSession robWinchSession = save(touch(setAttribute(createSession("robWinch"), "vip", "yes")));
ExpiringSession jonDoeSession = save(touch(setAttribute(createSession("jonDoe"), "vip", "no")));
ExpiringSession pieDoeSession = save(touch(setAttribute(createSession("pieDoe"), "viper", "true")));
ExpiringSession sourDoeSession = save(touch(createSession("sourDoe")));
assertThat(get(johnBlumSession.getId())).isEqualTo(johnBlumSession);
assertThat(johnBlumSession.getAttribute("vip")).isEqualTo("yes");
assertThat(get(robWinchSession.getId())).isEqualTo(robWinchSession);
assertThat(robWinchSession.getAttribute("vip")).isEqualTo("yes");
assertThat(get(jonDoeSession.getId())).isEqualTo(jonDoeSession);
assertThat(jonDoeSession.getAttribute("vip")).isEqualTo("no");
assertThat(get(pieDoeSession.getId())).isEqualTo(pieDoeSession);
assertThat(pieDoeSession.getAttributeNames().contains("vip")).isFalse();
assertThat(get(sourDoeSession.getId())).isEqualTo(sourDoeSession);
assertThat(sourDoeSession.getAttributeNames().contains("vip")).isFalse();
Map<String, ExpiringSession> vipSessions = doFindByIndexNameAndIndexValue("vip", "yes");
assertThat(vipSessions).isNotNull();
assertThat(vipSessions.size()).isEqualTo(2);
assertThat(vipSessions.get(johnBlumSession.getId())).isEqualTo(johnBlumSession);
assertThat(vipSessions.get(robWinchSession.getId())).isEqualTo(robWinchSession);
assertThat(vipSessions.containsKey(jonDoeSession.getId()));
assertThat(vipSessions.containsKey(pieDoeSession.getId()));
assertThat(vipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> nonVipSessions = doFindByIndexNameAndIndexValue("vip", "no");
assertThat(nonVipSessions).isNotNull();
assertThat(nonVipSessions.size()).isEqualTo(1);
assertThat(nonVipSessions.get(jonDoeSession.getId())).isEqualTo(jonDoeSession);
assertThat(nonVipSessions.containsKey(johnBlumSession.getId()));
assertThat(nonVipSessions.containsKey(robWinchSession.getId()));
assertThat(nonVipSessions.containsKey(pieDoeSession.getId()));
assertThat(nonVipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> noSessions = doFindByIndexNameAndIndexValue("nonExistingAttribute", "test");
assertThat(noSessions).isNotNull();
assertThat(noSessions.isEmpty()).isTrue();
}
@Test
public void findSessionsByPrincipalName() {
ExpiringSession sessionOne = save(touch(createSession("robWinch")));
@@ -212,7 +268,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
}
@Test
public void doesNotFindAfterPrincipalRemoved() {
public void findsNoSessionsAfterPrincipalIsRemoved() {
String username = "doesNotFindAfterPrincipalRemoved";
ExpiringSession session = save(touch(createSession(username)));
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, null);
@@ -271,6 +327,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
@SuppressWarnings("unused")
static class SpringSessionGemFireConfiguration {
@Bean
@@ -288,6 +345,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
@@ -296,7 +354,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
}
}
public static class Person implements PdxSerializable {
public static class Person implements Comparable<Person>, PdxSerializable {
private String firstName;
private String lastName;
@@ -336,6 +394,12 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
this.lastName = pdxReader.readString("lastName");
}
@SuppressWarnings("all")
public int compareTo(final Person person) {
int compareValue = getLastName().compareTo(person.getLastName());
return (compareValue != 0 ? compareValue : getFirstName().compareTo(person.getFirstName()));
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.session.data.gemfire.config.annotation.web.http;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Properties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionShortcut;
import com.gemstone.gemfire.cache.query.Index;
import com.gemstone.gemfire.cache.query.QueryService;
/**
* The GemFireHttpSessionJavaConfigurationTests class...
*
* @author John Blum
* @since 1.0.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireHttpSessionJavaConfigurationTests extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache, String regionName, DataPolicy dataPolicy) {
assertThat(GemFireUtils.isPeer(gemfireCache)).isTrue();
Region<K, V> region = gemfireCache.getRegion(regionName);
assertRegion(region, regionName, dataPolicy);
return region;
}
@Test
public void gemfireCacheConfigurationIsValid() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example",
DataPolicy.REPLICATE);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 900);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example",
DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index principalNameIndex = queryService.getIndex(example, "principalNameIndex");
assertIndex(principalNameIndex, "principalName", example.getFullPath());
}
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasNotCreated() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example",
DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
assertThat(sessionAttributesIndex).isNull();
}
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900,
regionName = "Example", serverRegionShortcut = RegionShortcut.REPLICATE)
public static class GemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", GemFireHttpSessionJavaConfigurationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "warning");
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean cacheFactory = new CacheFactoryBean();
cacheFactory.setClose(true);
cacheFactory.setProperties(gemfireProperties());
cacheFactory.setUseBeanFactoryLocator(false);
return cacheFactory;
}
}
}

View File

@@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@@ -31,9 +32,11 @@ import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.query.Index;
import com.gemstone.gemfire.cache.query.QueryService;
/**
* The GemFireHttpSessionConfigurationXmlTests class is a test suite of test cases testing the configuration of
* The GemFireHttpSessionXmlConfigurationTests class is a test suite of test cases testing the configuration of
* Spring Session backed by GemFire using XML configuration meta-data.
*
* @author John Blum
@@ -48,19 +51,53 @@ import com.gemstone.gemfire.cache.Region;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class GemFireHttpSessionConfigurationXmlTests 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);
assertRegion(region, regionName, dataPolicy);
return region;
}
@Test
public void gemfireCacheConfigurationIsValid() {
assertThat(gemfireCache).isNotNull();
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example", DataPolicy.NORMAL);
Region<Object, ExpiringSession> example = gemfireCache.getRegion("Example");
assertRegion(example, "Example", DataPolicy.NORMAL);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 3600);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index principalNameIndex = queryService.getIndex(example, "principalNameIndex");
assertIndex(principalNameIndex, "principalName", example.getFullPath());
}
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(gemfireCache, "Example", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
assertIndex(sessionAttributesIndex, "s.attributes['one', 'two', 'three']",
String.format("%1$s s", example.getFullPath()));
}
}

View File

@@ -21,10 +21,13 @@ import java.io.DataOutput;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -325,6 +328,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
protected static final DateFormat TO_STRING_DATE_FORMAT = new SimpleDateFormat("YYYY-MM-dd-HH-mm-ss");
protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
static {
Instantiator.register(new Instantiator(GemFireSession.class, 800813552) {
@Override public DataSerializable newInstance() {
@@ -333,10 +338,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
});
}
private String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private SpelExpressionParser parser = new SpelExpressionParser();
private transient boolean delta = false;
private int maxInactiveIntervalInSeconds;
@@ -346,6 +347,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
private transient final GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(this);
private transient final SpelExpressionParser parser = new SpelExpressionParser();
private String id;
/* (non-Javadoc) */
@@ -426,6 +429,11 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
return sessionAttributes.getAttributeNames();
}
/* (non-Javadoc) */
public GemFireSessionAttributes getAttributes() {
return sessionAttributes;
}
/* (non-Javadoc) */
public synchronized boolean isExpired() {
long lastAccessedTime = getLastAccessedTime();
@@ -464,21 +472,23 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
/* (non-Javadoc) */
public synchronized void setPrincipalName(String principalName) {
setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
setAttribute(PRINCIPAL_NAME_INDEX_NAME, principalName);
}
/* (non-Javadoc) */
public synchronized String getPrincipalName() {
String principalName = getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
if(principalName != null) {
return principalName;
String principalName = getAttribute(PRINCIPAL_NAME_INDEX_NAME);
if (principalName == null) {
Object authentication = getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = parser.parseExpression("authentication?.name");
principalName = expression.getValue(authentication, String.class);
}
}
Object authentication = getAttribute(SPRING_SECURITY_CONTEXT);
if(authentication != null) {
Expression expression = parser.parseExpression("authentication?.name");
return expression.getValue(authentication, String.class);
}
return null;
return principalName;
}
/* (non-Javadoc) */
@@ -597,17 +607,20 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* The GemFireSessionAttributes class is a container for a Session attributes that implements both
* The GemFireSessionAttributes class is a container for Session attributes that implements both
* the {@link DataSerializable} and {@link Delta} GemFire interfaces for efficient storage and distribution
* (replication) in GemFire.
* (replication) in GemFire. Additionally, GemFireSessionAttributes extends {@link AbstractMap} providing
* {@link Map}-like behavior since attributes of a Session are effectively a name to value mapping.
*
* @see java.util.AbstractMap
* @see com.gemstone.gemfire.DataSerializable
* @see com.gemstone.gemfire.DataSerializer
* @see com.gemstone.gemfire.Delta
* @see com.gemstone.gemfire.Instantiator
*/
@SuppressWarnings("serial")
public static class GemFireSessionAttributes implements DataSerializable, Delta {
public static class GemFireSessionAttributes extends AbstractMap<String, Object>
implements DataSerializable, Delta {
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
@@ -677,6 +690,21 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
return DEFAULT_ALLOW_JAVA_SERIALIZATION;
}
/* (non-Javadoc); NOTE: entrySet implementation is not Thread-safe. */
@Override
@SuppressWarnings("all")
public Set<Entry<String, Object>> entrySet() {
return new AbstractSet<Entry<String, Object>>() {
@Override public Iterator<Entry<String, Object>> iterator() {
return Collections.unmodifiableMap(sessionAttributes).entrySet().iterator();
}
@Override public int size() {
return sessionAttributes.size();
}
};
}
/* (non-Javadoc) */
public void from(Session session) {
synchronized (lock) {

View File

@@ -16,7 +16,6 @@
package org.springframework.session.data.gemfire;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -38,6 +37,10 @@ import com.gemstone.gemfire.cache.query.SelectResults;
*/
public class GemFireOperationsSessionRepository extends AbstractGemFireOperationsSessionRepository {
// GemFire OQL query used to lookup Sessions by arbitrary attributes.
protected static final String FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY =
"SELECT s FROM %1$s s WHERE s.attributes['%2$s'] = $1";
// GemFire OQL query used to look up Sessions by principal name.
protected static final String FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY =
"SELECT s FROM %1$s s WHERE s.principalName = $1";
@@ -54,19 +57,18 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
}
/**
* Looks up all the available Sessions tied to the specific user identified by principal name.
* Looks up all available Sessions with the particular attribute indexed by name having the given value.
*
* @param indexName the name of the indexed value (i.e. FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME).
* @param indexValue the value of the index to search for (i.e. username) to search for all existing Spring Sessions.
* @param indexName name of the indexed Session attribute.
* (e.g. {@link org.springframework.session.FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}).
* @param indexValue value of the indexed Session attribute to search on (e.g. username).
* @return a mapping of Session ID to Session instances.
* @see org.springframework.session.ExpiringSession
* @see java.util.Map
* @see #prepareQuery(String)
*/
public Map<String, ExpiringSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
if(!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
return Collections.emptyMap();
}
SelectResults<ExpiringSession> results = getTemplate().find(String.format(
FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, getFullyQualifiedRegionName()), indexValue);
SelectResults<ExpiringSession> results = getTemplate().find(prepareQuery(indexName), indexValue);
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
@@ -77,6 +79,18 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
return sessions;
}
/**
* Prepares the appropriate GemFire OQL query based on the indexed Session attribute name.
*
* @param indexName a String indicating the name of the indexed Session attribute.
* @return an appropriate GemFire OQL statement for querying on a particular indexed Session attribute.
*/
protected String prepareQuery(String indexName) {
return (PRINCIPAL_NAME_INDEX_NAME.equals(indexName)
? String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, getFullyQualifiedRegionName())
: String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY, getFullyQualifiedRegionName(), indexName));
}
/**
* Constructs a new {@link ExpiringSession} instance backed by GemFire.
*

View File

@@ -116,6 +116,14 @@ public @interface EnableGemFireHttpSession {
*/
ClientRegionShortcut clientRegionShortcut() default ClientRegionShortcut.PROXY;
/**
* Identifies the Session attributes by name that should be indexed for query operations.
* For instance, find all Sessions in GemFire having attribute A defined with value X.
*
* @return an array of Strings identifying the names of Session attributes to index.
*/
String[] indexableSessionAttributes() default {};
/**
* Defines the maximum interval in seconds that a Session can remain inactive before it is considered expired.
* Defaults to 1800 seconds, or 30 minutes.

View File

@@ -37,6 +37,7 @@ import org.springframework.session.data.gemfire.AbstractGemFireOperationsSession
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.support.GemFireUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.ExpirationAction;
@@ -68,6 +69,8 @@ import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
* @see com.gemstone.gemfire.cache.RegionAttributes
* @see com.gemstone.gemfire.cache.RegionShortcut
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
* @since 1.1.0
*/
@Configuration
@@ -85,6 +88,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
public static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME = "ClusteredSpringSessions";
public static final String[] DEFAULT_INDEXABLE_SESSION_ATTRIBUTES = new String[0];
private int maxInactiveIntervalInSeconds = DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
private ClassLoader beanClassLoader;
@@ -95,6 +100,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
private String springSessionGemFireRegionName = DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES;
/**
* Sets a reference to the {@link ClassLoader} used to load bean definition class types in a Spring context.
*
@@ -133,11 +140,55 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*
* @return the ClientRegionShortcut used to configure the GemFire ClientCache Region.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
* @see EnableGemFireHttpSession#clientRegionShortcut()
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (clientRegionShortcut != null ? clientRegionShortcut : DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Sets the names of all Session attributes that should be indexed by GemFire.
*
* @param indexableSessionAttributes an array of Strings indicating the names of all Session attributes
* for which an Index will be created by GemFire.
*/
public void setIndexableSessionAttributes(String[] indexableSessionAttributes) {
this.indexableSessionAttributes = indexableSessionAttributes;
}
/**
* Get the names of all Session attributes that should be indexed by GemFire.
*
* @return an array of Strings indicating the names of all Session attributes for which an Index
* will be created by GemFire. Defaults to an empty String array if unspecified.
* @see EnableGemFireHttpSession#indexableSessionAttributes()
*/
protected String[] getIndexableSessionAttributes() {
return (indexableSessionAttributes != null ? indexableSessionAttributes : 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.
*
@@ -153,6 +204,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*
* @return an integer value specifying the maximum interval in seconds that a Session can remain inactive
* before it is considered expired.
* @see EnableGemFireHttpSession#maxInactiveIntervalInSeconds()
*/
protected int getMaxInactiveIntervalInSeconds() {
return maxInactiveIntervalInSeconds;
@@ -174,6 +226,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*
* @return the RegionShortcut used to configure the GemFire Cache Region.
* @see com.gemstone.gemfire.cache.RegionShortcut
* @see EnableGemFireHttpSession#serverRegionShortcut()
*/
protected RegionShortcut getServerRegionShortcut() {
return (serverRegionShortcut != null ? serverRegionShortcut : DEFAULT_SERVER_REGION_SHORTCUT);
@@ -195,6 +248,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
* @return a String specifying the name of the GemFire (Client)Cache Region
* used to store the Session.
* @see com.gemstone.gemfire.cache.Region#getName()
* @see EnableGemFireHttpSession#regionName()
*/
protected String getSpringSessionGemFireRegionName() {
return (StringUtils.hasText(springSessionGemFireRegionName) ? springSessionGemFireRegionName
@@ -216,6 +270,9 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
setClientRegionShortcut(ClientRegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes.getEnum(
"clientRegionShortcut")));
setIndexableSessionAttributes(enableGemFireHttpSessionAnnotationAttributes.getStringArray(
"indexableSessionAttributes"));
setMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAnnotationAttributes.getNumber(
"maxInactiveIntervalInSeconds").intValue());
@@ -359,7 +416,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
};
index.setCache(gemfireCache);
index.setName("principalNameIdx");
index.setName("principalNameIndex");
index.setExpression("principalName");
index.setFrom(GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName()));
index.setOverride(true);
@@ -368,4 +425,34 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
return index;
}
/**
* 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}.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a IndexFactoryBean creating 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();
}
}
};
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);
return index;
}
}

View File

@@ -1268,6 +1268,55 @@ public class AbstractGemFireOperationsSessionRepositoryTest {
verify(mockDataInput, times(1)).readUTF();
}
@Test
public void sessionAttributesEntrySetIteratesAttributeNameValues() {
GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes();
sessionAttributes.setAttribute("keyOne", "valueOne");
sessionAttributes.setAttribute("keyTwo", "valueTwo");
Set<Map.Entry<String, Object>> sessionAttributeEntries = sessionAttributes.entrySet();
assertThat(sessionAttributeEntries).isNotNull();
assertThat(sessionAttributeEntries.size()).isEqualTo(2);
Set<String> expectedNames = asSet("keyOne", "keyTwo");
Set<?> expectedValues = asSet("valueOne", "valueTwo");
for (Map.Entry<String, Object> entry : sessionAttributeEntries) {
expectedNames.remove(entry.getKey());
expectedValues.remove(entry.getValue());
}
assertThat(expectedNames.isEmpty()).isTrue();
assertThat(expectedValues.isEmpty()).isTrue();
sessionAttributes.setAttribute("keyThree", "valueThree");
assertThat(sessionAttributeEntries.size()).isEqualTo(3);
expectedNames = asSet("keyOne", "keyTwo");
expectedValues = asSet("valueOne", "valueTwo");
for (Map.Entry<String, Object> entry : sessionAttributeEntries) {
expectedNames.remove(entry.getKey());
expectedValues.remove(entry.getValue());
}
assertThat(expectedNames.isEmpty()).isTrue();
assertThat(expectedValues.isEmpty()).isTrue();
sessionAttributes.removeAttribute("keyOne");
sessionAttributes.removeAttribute("keyTwo");
assertThat(sessionAttributeEntries.size()).isEqualTo(1);
Map.Entry<String, ?> entry = sessionAttributeEntries.iterator().next();
assertThat(entry.getKey()).isEqualTo("keyThree");
assertThat(entry.getValue()).isEqualTo("valueThree");
}
@Test
public void sessionWithAttributesAreThreadSafe() throws Throwable {
TestFramework.runOnce(new ThreadSafeSessionTest());

View File

@@ -27,6 +27,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
import static org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY;
import static org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY;
import static org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.GemFireSession;
import java.util.Arrays;
@@ -47,7 +50,6 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.gemfire.GemfireAccessor;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionDeletedEvent;
@@ -66,6 +68,7 @@ import com.gemstone.gemfire.cache.query.SelectResults;
* @see org.mockito.Mockito
* @see org.mockito.runners.MockitoJUnitRunner
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see com.gemstone.gemfire.cache.Region
* @since 1.1.0
*/
@RunWith(MockitoJUnitRunner.class)
@@ -110,6 +113,36 @@ public class GemFireOperationsSessionRepositoryTest {
verify(mockTemplate, times(1)).getRegion();
}
@Test
@SuppressWarnings("unchecked")
public void findByIndexNameValueFindsMatchingSession() {
ExpiringSession mockSession = mock(ExpiringSession.class, "MockSession");
when(mockSession.getId()).thenReturn("1");
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
when(mockSelectResults.asList()).thenReturn(Collections.<Object>singletonList(mockSession));
String indexName = "vip";
String indexValue = "rwinch";
String expectedQql = String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
sessionRepository.getFullyQualifiedRegionName(), indexName);
when(mockTemplate.find(eq(expectedQql), eq(indexValue))).thenReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = sessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(1);
assertThat(sessions.get("1")).isEqualTo(mockSession);
verify(mockTemplate, times(1)).find(eq(expectedQql), eq(indexValue));
verify(mockSelectResults, times(1)).asList();
verify(mockSession, times(1)).getId();
}
@Test
@SuppressWarnings("unchecked")
public void findByPrincipalNameFindsMatchingSessions() throws Exception {
@@ -127,12 +160,13 @@ public class GemFireOperationsSessionRepositoryTest {
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
String expectedOql = String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
sessionRepository.getFullyQualifiedRegionName());
when(mockTemplate.find(eq(expectedOql), eq(principalName))).thenReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
Map<String, ExpiringSession> sessions = sessionRepository.findByIndexNameAndIndexValue(
PRINCIPAL_NAME_INDEX_NAME, principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(3);
@@ -156,12 +190,13 @@ public class GemFireOperationsSessionRepositoryTest {
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
String expectedOql = String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
sessionRepository.getFullyQualifiedRegionName());
when(mockTemplate.find(eq(expectedOql), eq(principalName))).thenReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
Map<String, ExpiringSession> sessions = sessionRepository.findByIndexNameAndIndexValue(
PRINCIPAL_NAME_INDEX_NAME, principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.isEmpty()).isTrue();
@@ -170,6 +205,25 @@ public class GemFireOperationsSessionRepositoryTest {
verify(mockSelectResults, times(1)).asList();
}
@Test
public void prepareQueryReturnsPrincipalNameOql() {
String actualQql = sessionRepository.prepareQuery(PRINCIPAL_NAME_INDEX_NAME);
String expectedOql = String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
sessionRepository.getFullyQualifiedRegionName());
assertThat(actualQql).isEqualTo(expectedOql);
}
@Test
public void prepareQueryReturnsIndexNameValueOql() {
String attributeName = "testAttributeName";
String actualOql = sessionRepository.prepareQuery(attributeName);
String expectedOql = String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
sessionRepository.getFullyQualifiedRegionName(), attributeName);
assertThat(actualOql).isEqualTo(expectedOql);
}
@Test
public void createProperlyInitializedSession() {
final long beforeOrAtCreationTime = System.currentTimeMillis();

View File

@@ -47,6 +47,9 @@ import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.GemFireCache
@@ -58,6 +61,10 @@ public class GemFireHttpSessionConfigurationTest {
private GemFireHttpSessionConfiguration gemfireConfiguration;
protected <T> T[] toArray(T... array) {
return array;
}
@Before
public void setup() {
gemfireConfiguration = new GemFireHttpSessionConfiguration();
@@ -91,6 +98,29 @@ public class GemFireHttpSessionConfigurationTest {
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
}
@Test
public void setAndGetIndexableSessionAttributes() {
assertThat(gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
gemfireConfiguration.setIndexableSessionAttributes(toArray("one", "two", "three"));
assertThat(gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression())
.isEqualTo("'one', 'two', 'three'");
gemfireConfiguration.setIndexableSessionAttributes(toArray("one"));
assertThat(gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one"));
assertThat(gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("'one'");
gemfireConfiguration.setIndexableSessionAttributes(null);
assertThat(gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
assertThat(gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
}
@Test
public void setAndGetMaxInactiveIntervalInSeconds() {
assertThat(gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(
@@ -160,6 +190,7 @@ public class GemFireHttpSessionConfigurationTest {
Map<String, Object> annotationAttributes = new HashMap<String, Object>(4);
annotationAttributes.put("clientRegionShortcut", ClientRegionShortcut.CACHING_PROXY);
annotationAttributes.put("indexableSessionAttributes", toArray("one", "two", "three"));
annotationAttributes.put("maxInactiveIntervalInSeconds", 600);
annotationAttributes.put("serverRegionShortcut", RegionShortcut.REPLICATE);
annotationAttributes.put("regionName", "TEST");
@@ -170,6 +201,7 @@ public class GemFireHttpSessionConfigurationTest {
gemfireConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(gemfireConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(600);
assertThat(gemfireConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
assertThat(gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("TEST");

View File

@@ -11,15 +11,15 @@
">
<util:properties id="gemfireProperties">
<prop key="name">GemFireHttpSessionConfigurationXmlTests</prop>
<prop key="name">GemFireHttpSessionXmlConfigurationTests</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">warning</prop>
</util:properties>
<gfe:cache properties-ref="gemfireProperties"/>
<gfe:cache properties-ref="gemfireProperties" close="true"/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="3600" p:springSessionGemFireRegionName="Example"
p:indexableSessionAttributes="one, two, three" p:maxInactiveIntervalInSeconds="3600" p:springSessionGemFireRegionName="Example"
p:serverRegionShortcut="#{T(com.gemstone.gemfire.cache.RegionShortcut).LOCAL}"/>
</beans>