Add GemFire Support

Fixes GH PR #148 and PR #308 implementing a GemFire Adapter to support
clustered HttpSessions in Spring Session.

* Resolve SGF-373 - Implement a Spring Session Adapter for GemFire backing
a HttpSession similar to the Redis support.
* Add Spring Session annotation to enable GemFire support with
@EnableGemFireHttpSession.
* Add extesion of SpringHttpSessionConfiguration to configure GemFire using
GemFireHttpSessionConfiguration.
* Add implementation of SessionRepository to access clustered, replicated
HttpSession state in GemFire with GemFireOperationsSessionRepository.
* Utilize GemFire Data Serialization framework to both replicate
HttpSession state information as well as handle deltas.
* Utilize GemFire OQL query to lookup arbitrary Session attributes by name,
and in particular the user authenticated principal name.
* Implment unit and integration tests, and in particular, tests for both
peer-to-peer (p2p) and client/server topologies.
* Set initial Spring Data GemFire version to 1.7.2.RELEASE, which depends
on Pivotal GemFire 8.1.0.
* Add documentation, Javadoc and samples along with additional Integration
Tests.

Fixes gh-148
This commit is contained in:
John Blum
2016-01-28 13:19:18 -06:00
committed by Rob Winch
parent 1931da83a5
commit 019e0083b0
68 changed files with 8634 additions and 2 deletions

View File

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

View File

@@ -0,0 +1,371 @@
/*
* 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;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.session.data.gemfire.support.GemFireUtils;
import org.springframework.session.events.AbstractSessionEvent;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.Pool;
/**
* The ClientServerGemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test cases testing
* the functionality of GemFire-backed Spring Sessions using a GemFire client-server topology.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.client.ClientCache
* @see com.gemstone.gemfire.cache.client.Pool
* @see com.gemstone.gemfire.cache.server.CacheServer
* @since 1.0.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =
ClientServerGemFireOperationsSessionRepositoryIntegrationTests.SpringSessionGemFireClientConfiguration.class)
@WebAppConfiguration
@SuppressWarnings("unused")
public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
private static Process gemfireServer;
@Autowired
private SessionEventListener sessionEventListener;
@BeforeClass
public static void startGemFireServer() throws IOException {
final long t0 = System.currentTimeMillis();
final int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting GemFire Server running on [%1$s] listening on port [%2$d]%n",
InetAddress.getLocalHost().getHostName(), port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname = String.format("gemfire-client-server-tests-%1$s",
TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
//assertThat(waitForProcessToStart(gemfireServer, processWorkingDirectory)).isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
}
@AfterClass
public static void stopGemFireServerAndDeleteArtifacts() {
if (gemfireServer != null) {
gemfireServer.destroyForcibly();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue();
}
@Before
public void setup() {
assertThat(GemFireUtils.isClient(gemfireCache)).isTrue();
Region<Object, ExpiringSession> springSessionGemFireRegion = gemfireCache.getRegion(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(springSessionGemFireRegion).isNotNull();
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes =
springSessionGemFireRegion.getAttributes();
assertThat(springSessionGemFireRegionAttributes).isNotNull();
assertThat(springSessionGemFireRegionAttributes.getDataPolicy()).isEqualTo(DataPolicy.EMPTY);
}
@After
public void tearDown() {
sessionEventListener.getSessionEvent();
}
@Test
public void createSessionFiresSessionCreatedEvent() {
final long beforeOrAtCreationTime = System.currentTimeMillis();
ExpiringSession expectedSession = save(createSession());
AbstractSessionEvent sessionEvent = sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
ExpiringSession createdSession = sessionEvent.getSession();
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.getId()).isNotNull();
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
gemfireSessionRepository.delete(expectedSession.getId());
}
@Test
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
assertThat(sessionEventListener.getSessionEvent()).isNull();
ExpiringSession savedSession = gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
// NOTE for some reason or another, performing a GemFire (Client)Cache Region.get(key)
// causes a Region CREATE event... o.O
// calling sessionEventListener.getSessionEvent() here to clear the event
sessionEventListener.getSessionEvent();
sessionEvent = sessionEventListener.waitForSessionEvent(TimeUnit.SECONDS.toMillis(
MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession expiredSession = gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(expiredSession).isNull();
}
@Test
public void deleteExistingNonExpiredSessionFiresSessionDeletedEventAndReturnsNullOnGet() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
gemfireSessionRepository.delete(expectedSession.getId());
sessionEvent = sessionEventListener.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession deletedSession = gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(deletedSession).isNull();
}
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireClientConfiguration {
//TODO remove when SGF-458 is released
static {
System.setProperty("gemfire.log-level", GEMFIRE_LOG_LEVEL);
}
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
//TODO uncomment when SGF-458 is released.
//gemfireProperties.setProperty("name", ClientServerGemFireOperationsSessionRepositoryIntegrationTests.class.getName());
//gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"+DEFAULT_GEMFIRE_SERVER_PORT+"}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
// TODO uncomment when SGF-458 is released
//poolFactory.setProperties(gemfireProperties());
poolFactory.setName(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMaxConnections(SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(2);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setThreadLocalConnections(false);
poolFactory.setServerEndpoints(Collections.singletonList(new ConnectionEndpoint(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
ClientCacheFactoryBean gemfireCache(Pool gemfirePool) {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setPool(gemfirePool);
clientCacheFactory.setProperties(gemfireProperties());
clientCacheFactory.setUseBeanFactoryLocator(false);
return clientCacheFactory;
}
@Bean
public SessionEventListener sessionEventListener() {
return new SessionEventListener();
}
// used for debugging purposes
public static void main(final String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(
SpringSessionGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
ClientCache clientCache = applicationContext.getBean(ClientCache.class);
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
}
}
}
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireServerConfiguration {
static final int MAX_CONNECTIONS = 50;
static final String SERVER_HOSTNAME = "localhost";
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", SpringSessionGemFireServerConfiguration.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-file", "server.log");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean cacheFactory = new CacheFactoryBean();
cacheFactory.setProperties(gemfireProperties());
cacheFactory.setUseBeanFactoryLocator(false);
return cacheFactory;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:"+DEFAULT_GEMFIRE_SERVER_PORT+"}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
cacheServerFactory.setPort(port);
return cacheServerFactory;
}
public static void main(final String[] args) throws IOException {
new AnnotationConfigApplicationContext(SpringSessionGemFireServerConfiguration.class).registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}
}
}

View File

@@ -0,0 +1,304 @@
/*
* 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;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.GemFireSession;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.query.Query;
import com.gemstone.gemfire.cache.query.QueryService;
import com.gemstone.gemfire.cache.query.SelectResults;
import com.gemstone.gemfire.pdx.PdxReader;
import com.gemstone.gemfire.pdx.PdxSerializable;
import com.gemstone.gemfire.pdx.PdxWriter;
/**
* The GemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test cases testing
* the findByPrincipalName query method on the GemFireOpeationsSessionRepository class.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.web.WebAppConfiguration
* @since 1.1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@SuppressWarnings("unused")
public class GemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 300;
private static final String GEMFIRE_LOG_LEVEL = "warning";
private static final String SPRING_SESSION_GEMFIRE_REGION_NAME = "TestPartitionedSessions";
@Before
public void setup() {
assertThat(gemfireCache).isNotNull();
assertThat(gemfireSessionRepository).isNotNull();
assertThat(gemfireSessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(
MAX_INACTIVE_INTERVAL_IN_SECONDS);
Region<Object, ExpiringSession> sessionRegion = gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME, DataPolicy.PARTITION);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
protected Map<String, ExpiringSession> doFindByPrincipalName(String principalName) {
return gemfireSessionRepository.findByPrincipalName(principalName);
}
@SuppressWarnings("unchecked")
protected Map<String, ExpiringSession> doFindByPrincipalName(String regionName, String principalName) {
try {
Region<String, ExpiringSession> region = gemfireCache.getRegion(regionName);
assertThat(region).isNotNull();
QueryService queryService = region.getRegionService().getQueryService();
String queryString = String.format("SELECT s FROM %1$s s WHERE s.principalName = $1", region.getFullPath());
Query query = queryService.newQuery(queryString);
SelectResults<ExpiringSession> results = (SelectResults<ExpiringSession>) query.execute(
new Object[] { principalName });
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
for (ExpiringSession session : results.asList()) {
sessions.put(session.getId(), session);
}
return sessions;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean enableQueryDebugging() {
return true;
}
@Test
public void findSessionsByPrincipalName() {
ExpiringSession sessionOne = save(touch(createSession("robWinch")));
ExpiringSession sessionTwo = save(touch(createSession("johnBlum")));
ExpiringSession sessionThree = save(touch(createSession("robWinch")));
ExpiringSession sessionFour = save(touch(createSession("johnBlum")));
ExpiringSession sessionFive = save(touch(createSession("robWinch")));
assertThat(get(sessionOne.getId())).isEqualTo(sessionOne);
assertThat(get(sessionTwo.getId())).isEqualTo(sessionTwo);
assertThat(get(sessionThree.getId())).isEqualTo(sessionThree);
assertThat(get(sessionFour.getId())).isEqualTo(sessionFour);
assertThat(get(sessionFive.getId())).isEqualTo(sessionFive);
Map<String, ExpiringSession> johnBlumSessions = doFindByPrincipalName("johnBlum");
assertThat(johnBlumSessions).isNotNull();
assertThat(johnBlumSessions.size()).isEqualTo(2);
assertThat(johnBlumSessions.containsKey(sessionOne.getId())).isFalse();
assertThat(johnBlumSessions.containsKey(sessionThree.getId())).isFalse();
assertThat(johnBlumSessions.containsKey(sessionFive.getId())).isFalse();
assertThat(johnBlumSessions.get(sessionTwo.getId())).isEqualTo(sessionTwo);
assertThat(johnBlumSessions.get(sessionFour.getId())).isEqualTo(sessionFour);
Map<String, ExpiringSession> robWinchSessions = doFindByPrincipalName("robWinch");
assertThat(robWinchSessions).isNotNull();
assertThat(robWinchSessions.size()).isEqualTo(3);
assertThat(robWinchSessions.containsKey(sessionTwo.getId())).isFalse();
assertThat(robWinchSessions.containsKey(sessionFour.getId())).isFalse();
assertThat(robWinchSessions.get(sessionOne.getId())).isEqualTo(sessionOne);
assertThat(robWinchSessions.get(sessionThree.getId())).isEqualTo(sessionThree);
assertThat(robWinchSessions.get(sessionFive.getId())).isEqualTo(sessionFive);
}
@Test
public void findsNoSessionsByNonExistingPrincipal() {
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName("nonExistingPrincipalName");
assertThat(nonExistingPrincipalSessions).isNotNull();
assertThat(nonExistingPrincipalSessions.isEmpty()).isTrue();
}
@Test
public void saveAndReadSessionWithAttributes() {
ExpiringSession expectedSession = gemfireSessionRepository.createSession();
assertThat(expectedSession).isInstanceOf(GemFireSession.class);
((GemFireSession) expectedSession).setPrincipalName("jblum");
List<String> expectedAttributeNames = Arrays.asList(
"booleanAttribute", "numericAttribute", "stringAttribute", "personAttribute");
Person jonDoe = new Person("Jon", "Doe");
expectedSession.setAttribute(expectedAttributeNames.get(0), true);
expectedSession.setAttribute(expectedAttributeNames.get(1), Math.PI);
expectedSession.setAttribute(expectedAttributeNames.get(2), "test");
expectedSession.setAttribute(expectedAttributeNames.get(3), jonDoe);
gemfireSessionRepository.save(touch(expectedSession));
ExpiringSession savedSession = gemfireSessionRepository.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
assertThat(savedSession).isInstanceOf(GemFireSession.class);
assertThat(((GemFireSession) savedSession).getPrincipalName()).isEqualTo("jblum");
assertThat(savedSession.getAttributeNames().containsAll(expectedAttributeNames)).as(
String.format("Expected (%1$s); but was (%2$s)", expectedAttributeNames,savedSession.getAttributeNames()))
.isTrue();
assertThat(Boolean.valueOf(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(0))))).isTrue();
assertThat(Double.valueOf(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(1)))))
.isEqualTo(Math.PI);
assertThat(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(2)))).isEqualTo("test");
assertThat(savedSession.getAttribute(expectedAttributeNames.get(3))).isEqualTo(jonDoe);
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", GemFireOperationsSessionRepositoryIntegrationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}
}
public static class Person implements PdxSerializable {
private String firstName;
private String lastName;
public Person() {
}
public Person(String firstName, String lastName) {
this.firstName = validate(firstName);
this.lastName = validate(lastName);
}
private String validate(String value) {
Assert.hasText(value, String.format("The String value (%1$s) must be specified!", value));
return value;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getName() {
return String.format("%1$s %2$s", getFirstName(), getLastName());
}
public void toData(PdxWriter pdxWriter) {
pdxWriter.writeString("firstName", getFirstName());
pdxWriter.writeString("lastName", getLastName());
}
public void fromData(final PdxReader pdxReader) {
this.firstName = pdxReader.readString("firstName");
this.lastName = pdxReader.readString("lastName");
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person that = (Person) obj;
return ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName())
&& ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName());
return hashValue;
}
@Override
public String toString() {
return getName();
}
}
}

View File

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

View File

@@ -0,0 +1,67 @@
/*
* 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 org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.ExpiringSession;
import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests;
import org.springframework.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;
/**
* The GemFireHttpSessionConfigurationXmlTests 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
* @see org.junit.Test
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.web.WebAppConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@SuppressWarnings("unused")
public class GemFireHttpSessionConfigurationXmlTests extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
@Test
public void gemfireCacheConfigurationIsValid() {
assertThat(gemfireCache).isNotNull();
Region<Object, ExpiringSession> example = gemfireCache.getRegion("Example");
assertRegion(example, "Example", DataPolicy.NORMAL);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 3600);
}
}