From 5945845f8776471df447413208ccc35a463c2c97 Mon Sep 17 00:00:00 2001 From: John Blum Date: Sat, 30 Sep 2017 14:05:24 -0700 Subject: [PATCH] Add integration test to assert that no Spring (Session Data GemFire/Geode) artifacts of any kind are need on the classpath of a GemFire/Geode Server when PDX Serialization is used. Resolves Issue #2. --- .../AbstractGemFireIntegrationTests.java | 108 +++++++++- ...NoServerConfigurationIntegrationTests.java | 202 ++++++++++++++++++ .../data/gemfire/server/GemFireServer.java | 125 +++++++++++ 3 files changed, 424 insertions(+), 11 deletions(-) create mode 100644 spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/pdx/SessionSerializationWithPdxRequiresNoServerConfigurationIntegrationTests.java create mode 100644 spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/server/GemFireServer.java diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractGemFireIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractGemFireIntegrationTests.java index 125055c..0463172 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractGemFireIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractGemFireIntegrationTests.java @@ -23,9 +23,12 @@ import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray; import java.io.File; import java.io.IOException; import java.net.Socket; +import java.net.URISyntaxException; +import java.net.URL; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -50,6 +53,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.session.Session; import org.springframework.session.data.gemfire.support.GemFireUtils; import org.springframework.session.events.AbstractSessionEvent; +import org.springframework.util.StringUtils; /** * {@link AbstractGemFireIntegrationTests} is an abstract base class encapsulating common functionality @@ -85,7 +89,7 @@ public abstract class AbstractGemFireIntegrationTests { 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"); + System.getProperty("spring.session.data.gemfire.log-file", "gemfire-server.log"); protected static final String GEMFIRE_LOG_LEVEL = System.getProperty("spring.session.data.gemfire.log-level", "error"); @@ -101,13 +105,50 @@ public abstract class AbstractGemFireIntegrationTests { System.setProperty("gemfire.Query.VERBOSE", String.valueOf(isQueryDebuggingEnabled())); } + /* (non-Javadoc) */ + protected static String buildClassPathContainingJarFiles(String... jarFilenames) { + + StringBuilder classpath = new StringBuilder(); + + stream(nullSafeArray(jarFilenames, String.class)).map(AbstractGemFireIntegrationTests::findJarInClasspath) + .forEach(classpathEntry -> classpathEntry.filter(StringUtils::hasText).ifPresent(it -> { + + if (classpath.length() > 0) { + classpath.append(File.pathSeparator); + } + + classpath.append(it); + })); + + return classpath.toString(); + } + + /* (non-Javadoc) */ + private static Optional findClassInFileSystem(Class type) { + + return Optional.ofNullable(type) + .map(AbstractGemFireIntegrationTests::getResourceName) + .map(resourceName -> type.getClassLoader().getResource(resourceName)); + } + + /* (non-Javadoc) */ + private static Optional findJarInClasspath(String jarFilename) { + return stream(nullSafeArray(System.getProperty("java.class.path").split(File.pathSeparator), String.class)) + .filter(element -> element.contains(jarFilename)).findFirst(); + } + + /* (non-Javadoc) */ + private static String getResourceName(Class type) { + return type.getName().replaceAll("\\.", "/").concat(".class"); + } + /* (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(); + .as(String.format("Failed to create directory [%s]", directory)).isTrue(); directory.deleteOnExit(); @@ -116,11 +157,15 @@ public abstract class AbstractGemFireIntegrationTests { /* (non-Javadoc) */ protected static List createJavaProcessCommandLine(Class type, String... args) { + return createJavaProcessCommandLine(System.getProperty("java.class.path"), type, args); + } + + /* (non-Javadoc) */ + protected static List createJavaProcessCommandLine(String classpath, Class type, String... args) { List commandLine = new ArrayList<>(); String javaHome = System.getProperty("java.home"); - String javaExe = new File(new File(javaHome, "bin"), "java").getAbsolutePath(); commandLine.add(javaExe); @@ -131,28 +176,67 @@ public abstract class AbstractGemFireIntegrationTests { 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(classpath); commandLine.add(type.getName()); commandLine.addAll(extractProgramArguments(args)); - // System.err.printf("Java process command-line is (%1$s)%n", commandLine); + //System.err.printf("Java process command-line is [%s]%n", commandLine); return commandLine; } /* (non-Javadoc) */ - protected static List extractJvmArguments(String... args) { + private static List extractJvmArguments(String... args) { return stream(args).filter(arg -> arg.startsWith("-")).collect(Collectors.toList()); } /* (non-Javadoc) */ - protected static List extractProgramArguments(String... args) { + private static List extractProgramArguments(String... args) { return stream(args).filter(arg -> !arg.startsWith("-")).collect(Collectors.toList()); } - /* (non-Javadoc) */ + // Run Java Class in Directory with Arguments + protected static String resolveClasspath(String classpath, Class type) { + + return Optional.ofNullable(classpath) + .filter(StringUtils::hasText) + .filter(it -> type != null) + .flatMap(it -> findClassInFileSystem(type)) + .map(url -> { + try { + return new File(url.toURI()); + } + catch (URISyntaxException ignore) { + return null; + } + }) + .map(File::getAbsolutePath) + .map(pathname -> { + + int indexOfTypeName = pathname.indexOf(getResourceName(type)); + + pathname = (indexOfTypeName > -1 ? pathname.substring(0, indexOfTypeName) : pathname); + pathname = (pathname.endsWith(File.separator) ? pathname.substring(0, pathname.length() - 1) : pathname); + + return pathname; + }) + .map(location -> classpath.concat(File.pathSeparator).concat(location)) + .orElse(classpath); + } + + // Run Java Class in Directory with Arguments protected static Process run(Class type, File directory, String... args) throws IOException { - return new ProcessBuilder().command(createJavaProcessCommandLine(type, args)).directory(directory).start(); + return run(createJavaProcessCommandLine(type, args), directory); + } + + // Run Java Class using Classpath in Directory with Arguments + protected static Process run(String classpath, Class type, File directory, String... args) throws IOException { + return run(createJavaProcessCommandLine(resolveClasspath(classpath, type), type, args), directory); + } + + /* (non-Javadoc) */ + private static Process run(List command, File directory) throws IOException { + return new ProcessBuilder().command(command).directory(directory).inheritIO().redirectErrorStream(true).start(); } /* (non-Javadoc) */ @@ -188,9 +272,9 @@ public abstract class AbstractGemFireIntegrationTests { Socket socket = null; try { - if (!connected.get()) { + if (!this.connected.get()) { socket = new Socket(host, port); - connected.set(true); + this.connected.set(true); } } catch (IOException ignore) { @@ -214,7 +298,9 @@ public abstract class AbstractGemFireIntegrationTests { /* (non-Javadoc) */ protected static boolean waitForClientCacheToClose(long duration) { + try { + ClientCache clientCache = ClientCacheFactory.getAnyInstance(); clientCache.close(); diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/pdx/SessionSerializationWithPdxRequiresNoServerConfigurationIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/pdx/SessionSerializationWithPdxRequiresNoServerConfigurationIntegrationTests.java new file mode 100644 index 0000000..2c39c64 --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/serialization/pdx/SessionSerializationWithPdxRequiresNoServerConfigurationIntegrationTests.java @@ -0,0 +1,202 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.gemfire.serialization.pdx; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.Optional; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.apache.geode.cache.server.CacheServer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer; +import org.springframework.data.gemfire.support.ConnectionEndpoint; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests; +import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; +import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository; +import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; +import org.springframework.session.data.gemfire.server.GemFireServer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.SocketUtils; + +/** + * Integration tests asserting that a GemFire/Geode Server does not require any Spring Session Data GemFire/Geode + * dependencies or any transitive dependencies when PDX serialization is in effect. + * + * /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/bin/java -server -ea + * -Dgemfire.log-level=FINEST -Dgemfire.Query.VERBOSE=false -Dspring.session.data.gemfire.cache.server.port=34095 + * -classpath /Users/jblum/.gradle/caches/modules-2/files-2.1/antlr/antlr/2.7.7/83cd2cd674a217ade95a4bb83a8a14f351f48bd0/antlr-2.7.7.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/commons-collections/commons-collections/3.2.2/8ad72fe39fa8c91eaaf12aadb21e0c3661fe26d5/commons-collections-3.2.2.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/2.5/2852e6e05fbb95076fc091f6d1780f1f8fe35e0f/commons-io-2.5.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/commons-lang/commons-lang/2.6/ce1edb914c94ebc388f086c6827e8bdeec71ac2/commons-lang-2.6.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/io.github.lukehutch/fast-classpath-scanner/2.0.11/ae34a7a5e6de8ad1f86e12f6f7ae1869fcfe9987/fast-classpath-scanner-2.0.11.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/it.unimi.dsi/fastutil/7.1.0/9835253257524c1be7ab50c057aa2d418fb72082/fastutil-7.1.0.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/javax.resource/javax.resource-api/1.7/ae40e0864eb1e92c48bf82a2a3399cbbf523fb79/javax.resource-api-1.7.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/javax.transaction/javax.transaction-api/1.2/d81aff979d603edd90dcd8db2abc1f4ce6479e3e/javax.transaction-api-1.2.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna/4.5.0/55b548d3195efc5280bf1c3f17b49659c54dee40/jna-4.5.0.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.9.1/7a2999229464e7a324aa503c0a52ec0f05efe7bd/log4j-api-2.9.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.9.1/c041978c686866ee8534f538c6220238db3bb6be/log4j-core-2.9.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.geode/geode-common/1.2.1/9db253081d33f424f6e3ce0cde4b306e23e3420b/geode-common-1.2.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.geode/geode-core/1.2.1/fe853317e33dd2a1c291f29cee3c4be549f75a69/geode-core-1.2.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.geode/geode-json/1.2.1/bdb4c262e4ce6bb3b22e0f511cfb133a65fa0c04/geode-json-1.2.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.1/60077fe98b11e4e7cf8af9b20609326a166d6ac4/jackson-core-2.9.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.jgroups/jgroups/3.6.10.Final/fc0ff5a8a9de27ab62939956f705c2909bf86bc2/jgroups-3.6.10.Final.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.lucene/lucene-core/6.4.1/2a18924b9e0ed86b318902cb475a0b9ca4d7be5b/lucene-core-6.4.1.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.apache.shiro/shiro-core/1.4.0/6d05bd17e057fc12d278bb367c27f9cb0f3dc197/shiro-core-1.4.0.jar + * :/Users/jblum/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar + * :spring-session-data-geode/build/classes/integrationTest + * org.springframework.session.data.gemfire.server.GemFireServer + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.server.CacheServer + * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication + * @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer + * @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests + * @since 2.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class SessionSerializationWithPdxRequiresNoServerConfigurationIntegrationTests + extends AbstractGemFireIntegrationTests { + + private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + + private static File processWorkingDirectory; + + private static Process gemfireServer; + + private static final String GEMFIRE_LOG_LEVEL = "error"; + + @Autowired + private GemFireOperationsSessionRepository sessionRepository; + + @BeforeClass + public static void startGemFireServer() throws IOException { + + long t0 = System.currentTimeMillis(); + + int port = SocketUtils.findAvailableTcpPort(); + + System.err.printf("Starting a GemFire Server running on host [localhost] listening on port [%d]%n", port); + + System.setProperty("spring.session.data.gemfire.cache.server.port", String.valueOf(port)); + + String classpath = buildClassPathContainingJarFiles("javax.transaction-api", "antlr", + "commons-lang", "fastutil", "log4j-api", "log4j-core", "geode-common", "geode-core", "jgroups", + "shiro-core", "slf4j-api"); + + String processWorkingDirectoryPathname = + String.format("gemfire-server-pdx-serialization-tests-%1$s", TIMESTAMP.format(new Date())); + + processWorkingDirectory = createDirectory(processWorkingDirectoryPathname); + + gemfireServer = run(classpath, GemFireServer.class, + processWorkingDirectory, String.format("-Dspring.session.data.gemfire.cache.server.port=%d", port), + String.format("-Dgemfire.log-level=%s", GEMFIRE_LOG_LEVEL)); + + assertThat(waitForCacheServerToStart("localhost", port)).isTrue(); + + System.err.printf("GemFire Server [startup time = %d ms]%n", System.currentTimeMillis() - t0); + } + + @AfterClass + public static void stopGemFireServer() { + + Optional.ofNullable(gemfireServer).ifPresent(server -> { + + server.destroy(); + + System.err.printf("GemFire Server [exit code = %d]%n", + waitForProcessToStop(server, processWorkingDirectory)); + }); + + if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) { + FileSystemUtils.deleteRecursively(processWorkingDirectory); + } + + assertThat(waitForClientCacheToClose(DEFAULT_WAIT_DURATION)).isTrue(); + } + + @Test + public void sessionOperationsIsSuccessful() { + + Session session = save(createSession("jonDoe")); + + assertThat(session).isInstanceOf(GemFireSession.class); + assertThat(session.getId()).isNotNull(); + assertThat(session.getCreationTime()).isBeforeOrEqualTo(Instant.now()); + assertThat(session.isExpired()).isFalse(); + assertThat(((GemFireSession) session).getPrincipalName()).isEqualTo("jonDoe"); + + Session sessionById = get(session.getId()); + + assertThat(sessionById).isEqualTo(session); + assertThat(sessionById.isExpired()).isFalse(); + + Map sessionsByPrincipalName = this.sessionRepository.findByIndexNameAndIndexValue( + GemFireOperationsSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "jonDoe"); + + assertThat(sessionsByPrincipalName).hasSize(1); + + Session sessionByPrincipalName = sessionsByPrincipalName.values().iterator().next(); + + assertThat(sessionByPrincipalName).isInstanceOf(GemFireSession.class); + assertThat(sessionByPrincipalName).isEqualTo(session); + assertThat(sessionByPrincipalName.isExpired()).isFalse(); + assertThat(((GemFireSession) sessionByPrincipalName).getPrincipalName()).isEqualTo("jonDoe"); + } + + @ClientCacheApplication(logLevel = GEMFIRE_LOG_LEVEL, subscriptionEnabled = true) + @EnableGemFireHttpSession(poolName = "DEFAULT") + static class GemFireCacheClientConfiguration { + + @Bean + static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + @Bean + ClientCacheConfigurer clientCachePoolPortConfigurer( + @Value("${spring.session.data.gemfire.cache.server.port:" + CacheServer.DEFAULT_PORT + "}") int cacheServerPort) { + + return (beanName, clientCacheFactoryBean) -> clientCacheFactoryBean.setServers(Collections.singletonList( + new ConnectionEndpoint("localhost", cacheServerPort))); + } + } +} diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/server/GemFireServer.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/server/GemFireServer.java new file mode 100644 index 0000000..7458ab4 --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/server/GemFireServer.java @@ -0,0 +1,125 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.data.gemfire.server; + +import java.io.IOException; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.ExpirationAction; +import org.apache.geode.cache.ExpirationAttributes; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionFactory; +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.server.CacheServer; + +/** + * The {@link GemFireServer} class is a Java application class used to launch a GemFire Server + * with a peer {@link Cache}, a {@link CacheServer} and the {@literal ClusteredSpringSessions} + * {@link RegionShortcut#PARTITION} {@link Region}. + * + * @author John Blum + * @see java.util.Properties + * @see org.apache.geode.cache.Cache + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.server.CacheServer + * @since 2.0.0 + */ +public class GemFireServer implements Runnable { + + protected static final Integer GEMFIRE_CACHE_SERVER_PORT = + Integer.getInteger("spring.session.data.gemfire.cache.server.port", CacheServer.DEFAULT_PORT); + + public static void main(String[] args) { + newGemFireServer(args).run(); + } + + private final String[] args; + + public static GemFireServer newGemFireServer(String[] args) { + return new GemFireServer(args); + } + + protected GemFireServer(String[] args) { + this.args = Optional.ofNullable(args) + .orElseThrow(() -> new IllegalArgumentException("GemFireServer process arguments are required")); + } + + protected String[] getArguments() { + return this.args; + } + + @Override + public void run() { + run(getArguments()); + } + + @SuppressWarnings("unused") + protected void run(String[] args) { + createClusteredSpringSessionsRegion(addCacheServer(gemfireCache(gemfireProperties()))); + } + + protected Properties gemfireProperties() { + + Properties gemfireProperties = new Properties(); + + gemfireProperties.setProperty("name", "o.s.s.d.g.server.GemFireServer"); + gemfireProperties.setProperty("jmx-manager", "true"); + //gemfireProperties.setProperty("log-file", "gemfire-server.log"); + gemfireProperties.setProperty("log-level", "error"); + + return gemfireProperties; + } + + protected Cache gemfireCache(Properties gemfireProperties) { + return new CacheFactory(gemfireProperties).create(); + } + + protected Cache addCacheServer(Cache gemfireCache) { + + try { + CacheServer cacheServer = gemfireCache.addCacheServer(); + + cacheServer.setHostnameForClients("localhost"); + cacheServer.setPort(GEMFIRE_CACHE_SERVER_PORT); + cacheServer.start(); + + return gemfireCache; + } + catch (IOException cause) { + throw new RuntimeException("GemFire CacheServer failed to start", cause); + } + } + + protected Cache createClusteredSpringSessionsRegion(Cache gemfireCache) { + + RegionFactory clusteredSpringSessionsRegion = + gemfireCache.createRegionFactory(RegionShortcut.PARTITION); + + clusteredSpringSessionsRegion.setEntryIdleTimeout( + new ExpirationAttributes(Long.valueOf(TimeUnit.MINUTES.toSeconds(30)).intValue(), + ExpirationAction.INVALIDATE)); + + clusteredSpringSessionsRegion.create("ClusteredSpringSessions"); + + return gemfireCache; + } +}