This is an automated email from the ASF dual-hosted git repository.
jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 4fff1f9 GEODE-8094: Create HA test to ensure Spring Boot sessions
expire correctly (#5079)
4fff1f9 is described below
commit 4fff1f9fde1b1401c68722c015f65267bf5da971
Author: Jens Deppe <[email protected]>
AuthorDate: Fri May 15 08:29:25 2020 -0700
GEODE-8094: Create HA test to ensure Spring Boot sessions expire correctly
(#5079)
Co-authored-by: Murtuza Boxwala <[email protected]>
Co-authored-by: Raymond Ingles <[email protected]>
Co-authored-by: Jens Deppe <[email protected]>
---
.../src/test/resources/expected-pom.xml | 6 +
.../gradle/plugins/DependencyConstraints.groovy | 1 +
geode-redis/build.gradle | 7 +-
.../redis/session/RedisSessionDistDUnitTest.java | 118 ++++++++++++++
.../SessionDUnitTest.java} | 169 ++++++---------------
.../redis/session/SessionExpirationDUnitTest.java | 103 +++++++++++++
.../RedisSpringTestApplication.java | 2 +-
.../SessionController.java | 15 +-
.../config/DUnitSocketAddressResolver.java | 2 +-
.../config/SessionListener.java | 13 +-
.../config/WebMvcConfig.java | 2 +-
.../org/apache/geode/redis/internal/Command.java | 17 ++-
.../redis/internal/ExecutionHandlerContext.java | 32 ++++
13 files changed, 354 insertions(+), 133 deletions(-)
diff --git a/boms/geode-all-bom/src/test/resources/expected-pom.xml
b/boms/geode-all-bom/src/test/resources/expected-pom.xml
index 000c051..9eba879 100644
--- a/boms/geode-all-bom/src/test/resources/expected-pom.xml
+++ b/boms/geode-all-bom/src/test/resources/expected-pom.xml
@@ -861,6 +861,12 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-jetty</artifactId>
+ <version>2.2.1.RELEASE</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
diff --git
a/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
b/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
index eae28bd..15f3c09 100644
---
a/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
+++
b/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
@@ -263,6 +263,7 @@ class DependencyConstraints implements Plugin<Project> {
dependencySet(group: 'org.springframework.boot', version: '2.2.1.RELEASE')
{
entry('spring-boot-starter')
+ entry('spring-boot-starter-jetty')
entry('spring-boot-starter-web')
entry('spring-boot-starter-data-redis')
}
diff --git a/geode-redis/build.gradle b/geode-redis/build.gradle
index 3ef3ba7..97d5d54 100644
--- a/geode-redis/build.gradle
+++ b/geode-redis/build.gradle
@@ -58,11 +58,16 @@ dependencies {
acceptanceTestImplementation('org.testcontainers:testcontainers')
acceptanceTestRuntime(project(':geode-log4j'))
+ distributedTestCompile('org.apache.logging.log4j:log4j-core')
distributedTestImplementation(project(':geode-dunit'))
distributedTestImplementation(sourceSets.commonTest.output)
distributedTestImplementation('redis.clients:jedis')
-
distributedTestImplementation('org.springframework.boot:spring-boot-starter-web')
+ distributedTestCompile('javax.servlet:javax.servlet-api')
+
distributedTestImplementation('org.springframework.boot:spring-boot-starter-jetty')
+
distributedTestImplementation('org.springframework.boot:spring-boot-starter-web')
{
+ exclude module: 'spring-boot-starter-tomcat'
+ }
distributedTestImplementation('org.springframework.boot:spring-boot-starter')
distributedTestImplementation('org.springframework.boot:spring-boot-starter-data-redis')
distributedTestImplementation('org.springframework.session:spring-session-data-redis')
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/RedisSessionDistDUnitTest.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/RedisSessionDistDUnitTest.java
new file mode 100644
index 0000000..eedc990
--- /dev/null
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/RedisSessionDistDUnitTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Map;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.RedisTest;
+
+@Category({RedisTest.class})
+public class RedisSessionDistDUnitTest extends SessionDUnitTest {
+
+ @BeforeClass
+ public static void setup() {
+ SessionDUnitTest.setup();
+ startSpringApp(APP1, SERVER1, SERVER2, DEFAULT_SESSION_TIMEOUT);
+ startSpringApp(APP2, SERVER2, SERVER1, DEFAULT_SESSION_TIMEOUT);
+ }
+
+ @Test
+ public void should_beAbleToCreateASession_storedInRedis() {
+ String sessionCookie = createNewSessionWithNote(APP1, "note1");
+ String sessionId = getSessionId(sessionCookie);
+
+ Map<String, String> sessionInfo =
+ jedisConnetedToServer1.hgetAll("spring:session:sessions:" + sessionId);
+
+ assertThat(sessionInfo.get("sessionAttr:NOTES")).contains("note1");
+ }
+
+ @Test
+ public void should_storeSession() {
+ String sessionCookie = createNewSessionWithNote(APP1, "note1");
+
+ String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("note1");
+ }
+
+ @Test
+ public void should_propagateSession_toOtherServers() {
+ String sessionCookie = createNewSessionWithNote(APP1, "noteFromClient1");
+
+ String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("noteFromClient1");
+ }
+
+ @Test
+ public void should_getSessionFromServer1_whenServer2GoesDown() {
+ String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
+ cluster.crashVM(SERVER2);
+ try {
+ String[] sessionNotes = getSessionNotes(APP1, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("noteFromClient2");
+ } finally {
+ startRedisServer(SERVER2);
+ }
+ }
+
+ @Test
+ public void should_getSessionFromServer_whenServerGoesDownAndIsRestarted() {
+ String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
+ cluster.crashVM(SERVER2);
+ addNoteToSession(APP1, sessionCookie, "noteFromClient1");
+ startRedisServer(SERVER2);
+
+ String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("noteFromClient2",
"noteFromClient1");
+ }
+
+ @Test
+ public void should_getSession_whenServer2GoesDown_andAppFailsOverToServer1()
{
+ String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
+ cluster.crashVM(SERVER2);
+
+ try {
+ String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("noteFromClient2");
+ } finally {
+ startRedisServer(SERVER2);
+ }
+ }
+
+ @Test
+ public void
should_getSessionCreatedByApp2_whenApp2GoesDown_andClientConnectsToApp1() {
+ String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
+ stopSpringApp(APP2);
+
+ try {
+ String[] sessionNotes = getSessionNotes(APP1, sessionCookie);
+
+ assertThat(sessionNotes).containsExactly("noteFromClient2");
+ } finally {
+ startSpringApp(APP2, SERVER1, SERVER2, DEFAULT_SESSION_TIMEOUT);
+ }
+ }
+}
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/RedisSessionDistDUnitTest.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionDUnitTest.java
similarity index 53%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/RedisSessionDistDUnitTest.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionDUnitTest.java
index 3982d56..80114d4 100644
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/RedisSessionDistDUnitTest.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionDUnitTest.java
@@ -12,11 +12,9 @@
* or implied. See the License for the specific language governing permissions
and limitations under
* the License.
*/
-package org.apache.geode.redis;
-import static org.assertj.core.api.Assertions.assertThat;
+package org.apache.geode.redis.session;
-import java.io.Serializable;
import java.net.HttpCookie;
import java.util.Base64;
import java.util.HashMap;
@@ -24,13 +22,15 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configurator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity;
@@ -40,33 +40,37 @@ import org.springframework.web.client.RestTemplate;
import redis.clients.jedis.Jedis;
import org.apache.geode.internal.AvailablePortHelper;
-import
org.apache.geode.redis.springRedisTestApplication.RedisSpringTestApplication;
+import org.apache.geode.logging.internal.log4j.api.FastLogger;
+import
org.apache.geode.redis.session.springRedisTestApplication.RedisSpringTestApplication;
import org.apache.geode.test.awaitility.GeodeAwaitility;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.DistributedRestoreSystemProperties;
-import org.apache.geode.test.junit.categories.RedisTest;
-@Category({RedisTest.class})
-public class RedisSessionDistDUnitTest implements Serializable {
+public abstract class SessionDUnitTest {
@ClassRule
- public static ClusterStartupRule cluster = new ClusterStartupRule(5);
+ public static ClusterStartupRule cluster = new ClusterStartupRule();
@Rule
public DistributedRestoreSystemProperties restoreSystemProperties =
new DistributedRestoreSystemProperties();
- public static ConfigurableApplicationContext springApplicationContext;
- private static final int LOCATOR = 0;
- private static final int SERVER1 = 1;
- private static final int SERVER2 = 2;
- private static final int APP1 = 3;
- private static final int APP2 = 4;
+ // 30 minutes
+ protected static final int DEFAULT_SESSION_TIMEOUT = 60 * 30;
+
+ protected static final int LOCATOR = 0;
+ protected static final int SERVER1 = 1;
+ protected static final int SERVER2 = 2;
+ protected static final int APP1 = 3;
+ protected static final int APP2 = 4;
+
+
private static final Map<Integer, Integer> ports = new HashMap<>();
+ public static ConfigurableApplicationContext springApplicationContext;
- private static Jedis jedis;
+ protected static Jedis jedisConnetedToServer1;
private static final int JEDIS_TIMEOUT =
Math.toIntExact(GeodeAwaitility.getTimeout().toMillis());
@BeforeClass
@@ -80,137 +84,62 @@ public class RedisSessionDistDUnitTest implements
Serializable {
cluster.startLocatorVM(LOCATOR);
startRedisServer(SERVER1);
startRedisServer(SERVER2);
- startSpringApp(APP1, SERVER1, SERVER2);
- startSpringApp(APP2, SERVER2, SERVER1);
- jedis = new Jedis("localhost", ports.get(SERVER1), JEDIS_TIMEOUT);
- }
-
- @After
- public void cleanupAfterTest() {
- jedis.flushAll();
+ jedisConnetedToServer1 = new Jedis("localhost", ports.get(SERVER1),
JEDIS_TIMEOUT);
}
@AfterClass
public static void cleanupAfterClass() {
- jedis.disconnect();
- }
-
- @Test
- public void should_beAbleToCreateASession_storedInRedis() {
- String sessionCookie = createNewSessionWithNote(APP1, "note1");
- String sessionId = getSessionId(sessionCookie);
-
- Map<String, String> sessionInfo =
- jedis.hgetAll("spring:session:sessions:" + sessionId);
-
- assertThat(sessionInfo.get("sessionAttr:NOTES")).contains("note1");
- }
-
- @Test
- public void should_storeSession() {
- String sessionCookie = createNewSessionWithNote(APP1, "note1");
-
- String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("note1");
- }
-
- @Test
- public void should_propagateSession_toOtherServers() {
- String sessionCookie = createNewSessionWithNote(APP1, "noteFromClient1");
-
- String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("noteFromClient1");
- }
-
- @Test
- public void should_getSessionFromServer1_whenServer2GoesDown() {
- String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
- cluster.crashVM(SERVER2);
- try {
- String[] sessionNotes = getSessionNotes(APP1, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("noteFromClient2");
- } finally {
- startRedisServer(SERVER2);
- }
- }
-
- @Test
- public void should_getSessionFromServer_whenServerGoesDownAndIsRestarted() {
- String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
- cluster.crashVM(SERVER2);
- addNoteToSession(APP1, sessionCookie);
- startRedisServer(SERVER2);
-
- String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("noteFromClient2",
"noteFromClient1");
- }
-
- @Test
- public void should_getSession_whenServer2GoesDown_andAppFailsOverToServer1()
{
- String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
- cluster.crashVM(SERVER2);
-
- try {
- String[] sessionNotes = getSessionNotes(APP2, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("noteFromClient2");
- } finally {
- startRedisServer(SERVER2);
- }
- }
-
- @Test
- public void
should_getSessionCreatedByApp2_whenApp2GoesDown_andClientConnectsToApp1() {
- String sessionCookie = createNewSessionWithNote(APP2, "noteFromClient2");
+ jedisConnetedToServer1.disconnect();
+ stopSpringApp(APP1);
stopSpringApp(APP2);
+ }
- try {
- String[] sessionNotes = getSessionNotes(APP1, sessionCookie);
-
- assertThat(sessionNotes).containsExactly("noteFromClient2");
- } finally {
- startSpringApp(APP2, SERVER1, SERVER2);
- }
-
+ @After
+ public void cleanupAfterTest() {
+ jedisConnetedToServer1.flushAll();
}
- private static void startRedisServer(int server1) {
- cluster.startServerVM(server1, redisProperties(server1),
+ protected static void startRedisServer(int server) {
+ cluster.startServerVM(server, redisProperties(server),
cluster.getMember(LOCATOR).getPort());
+
+ cluster.getVM(server).invoke("Set logging level to DEBUG", () -> {
+ Logger logger = LogManager.getLogger("org.apache.geode.redis.internal");
+ Configurator.setAllLevels(logger.getName(), Level.getLevel("DEBUG"));
+ FastLogger.setDelegating(true);
+ });
}
- private static Properties redisProperties(int server2) {
+ private static Properties redisProperties(int server) {
Properties redisPropsForServer = new Properties();
redisPropsForServer.setProperty("redis-bind-address", "localHost");
- redisPropsForServer.setProperty("redis-port", "" + ports.get(server2));
- redisPropsForServer.setProperty("log-level", "warn");
+ redisPropsForServer.setProperty("redis-port", "" + ports.get(server));
+ redisPropsForServer.setProperty("log-level", "info");
return redisPropsForServer;
}
- private static void startSpringApp(int sessionApp, int primaryServer, int
secondaryServer) {
+ static void startSpringApp(int sessionApp, int primaryServer, int
secondaryServer,
+ long sessionTimeout) {
int primaryRedisPort = ports.get(primaryServer);
int failoverRedisPort = ports.get(secondaryServer);
int httpPort = ports.get(sessionApp);
VM host = cluster.getVM(sessionApp);
- host.invoke("start a spring app", () -> {
+ host.invoke("Start a Spring app", () -> {
System.setProperty("server.port", "" + httpPort);
System.setProperty("spring.redis.port", "" + primaryRedisPort);
+ System.setProperty("server.servlet.session.timeout", "" + sessionTimeout
+ "s");
springApplicationContext = SpringApplication.run(
RedisSpringTestApplication.class,
"" + primaryRedisPort, "" + failoverRedisPort);
});
}
- private static void stopSpringApp(int sessionApp) {
+ static void stopSpringApp(int sessionApp) {
cluster.getVM(sessionApp).invoke(() -> springApplicationContext.close());
}
- private String createNewSessionWithNote(int sessionApp, String note) {
+ protected String createNewSessionWithNote(int sessionApp, String note) {
HttpEntity<String> request = new HttpEntity<>(note);
HttpHeaders resultHeaders = new RestTemplate()
.postForEntity(
@@ -222,7 +151,7 @@ public class RedisSessionDistDUnitTest implements
Serializable {
return resultHeaders.getFirst("Set-Cookie");
}
- private String[] getSessionNotes(int sessionApp, String sessionCookie) {
+ protected String[] getSessionNotes(int sessionApp, String sessionCookie) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", sessionCookie);
HttpEntity<String> request2 = new HttpEntity<>("", requestHeaders);
@@ -236,10 +165,10 @@ public class RedisSessionDistDUnitTest implements
Serializable {
.getBody();
}
- private void addNoteToSession(int sessionApp, String sessionCookie) {
+ void addNoteToSession(int sessionApp, String sessionCookie, String note) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", sessionCookie);
- HttpEntity<String> request = new HttpEntity<>("noteFromClient1",
requestHeaders);
+ HttpEntity<String> request = new HttpEntity<>(note, requestHeaders);
new RestTemplate()
.postForEntity(
"http://localhost:" + ports.get(sessionApp) + "/addSessionNote",
@@ -248,7 +177,7 @@ public class RedisSessionDistDUnitTest implements
Serializable {
.getHeaders();
}
- private String getSessionId(String sessionCookie) {
+ protected String getSessionId(String sessionCookie) {
List<HttpCookie> cookies = HttpCookie.parse(sessionCookie);
byte[] decodedCookie =
Base64.getDecoder().decode(cookies.get(0).getValue());
return new String(decodedCookie);
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionExpirationDUnitTest.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionExpirationDUnitTest.java
new file mode 100644
index 0000000..0d6c000
--- /dev/null
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/SessionExpirationDUnitTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.redis.session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.apache.geode.test.awaitility.GeodeAwaitility;
+
+public class SessionExpirationDUnitTest extends SessionDUnitTest {
+
+ private static final int SHORT_SESSION_TIMEOUT = 5;
+
+ @BeforeClass
+ public static void setup() {
+ SessionDUnitTest.setup();
+ startSpringApp(APP1, SERVER1, SERVER2, SHORT_SESSION_TIMEOUT);
+ startSpringApp(APP2, SERVER2, SERVER1, SHORT_SESSION_TIMEOUT);
+ }
+
+ @Test
+ public void sessionShouldTimeout_whenRequestedFromSameServer() {
+ String sessionCookie = createNewSessionWithNote(APP1, "note1");
+ String sessionId = getSessionId(sessionCookie);
+
+ waitForTheSessionToExpire(sessionId);
+
+ assertThat(getSessionNotes(APP1, sessionCookie)).isNull();
+ }
+
+ @Test
+ public void sessionShouldTimeout_OnSecondaryServer() {
+ String sessionCookie = createNewSessionWithNote(APP1, "note1");
+ String sessionId = getSessionId(sessionCookie);
+
+ waitForTheSessionToExpire(sessionId);
+
+ assertThat(getSessionNotes(APP2, sessionCookie)).isNull();
+ }
+
+ @Test
+ @Ignore("GEODE-8058: this test needs to pass to have feature parity with
native redis")
+ public void
sessionShouldNotTimeoutOnFirstServer_whenAccessedOnSecondaryServer() {
+ String sessionCookie = createNewSessionWithNote(APP1, "note1");
+ String sessionId = getSessionId(sessionCookie);
+
+ refreshSession(sessionCookie, APP2);
+
+ assertThat(jedisConnetedToServer1.ttl("spring:session:sessions:expires:" +
sessionId))
+ .isGreaterThan(0);
+
+ assertThat(getSessionNotes(APP1, sessionCookie)).isNotNull();
+ }
+
+ @Test
+ @Ignore("GEODE-8058: this test needs to pass to have feature parity with
native redis")
+ public void sessionShouldTimeout_whenAppFailsOverToAnotherRedisServer() {
+ String sessionCookie = createNewSessionWithNote(APP2, "note1");
+ String sessionId = getSessionId(sessionCookie);
+
+ cluster.crashVM(SERVER2);
+
+ try {
+ waitForTheSessionToExpire(sessionId);
+
+ assertThat(getSessionNotes(APP1, sessionCookie)).isNull();
+ assertThat(getSessionNotes(APP2, sessionCookie)).isNull();
+
+ } finally {
+ startRedisServer(SERVER2);
+ }
+ }
+
+ private void waitForTheSessionToExpire(String sessionId) {
+ GeodeAwaitility.await().atMost((SHORT_SESSION_TIMEOUT + 5),
TimeUnit.SECONDS)
+ .until(
+ () ->
jedisConnetedToServer1.ttl("spring:session:sessions:expires:" + sessionId) < 0);
+ }
+
+ private void refreshSession(String sessionCookie, int sessionApp) {
+ GeodeAwaitility.await()
+ .during(SHORT_SESSION_TIMEOUT + 2, TimeUnit.SECONDS)
+ .until(() -> getSessionNotes(sessionApp, sessionCookie) != null);
+ }
+}
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/RedisSpringTestApplication.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/RedisSpringTestApplication.java
similarity index 94%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/RedisSpringTestApplication.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/RedisSpringTestApplication.java
index 088bee5..7c72528 100755
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/RedisSpringTestApplication.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/RedisSpringTestApplication.java
@@ -12,7 +12,7 @@
* or implied. See the License for the specific language governing permissions
and limitations under
* the License.
*/
-package org.apache.geode.redis.springRedisTestApplication;
+package org.apache.geode.redis.session.springRedisTestApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/SessionController.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/SessionController.java
similarity index 85%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/SessionController.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/SessionController.java
index 8d80fab..d354618 100644
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/SessionController.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/SessionController.java
@@ -12,12 +12,13 @@
* or implied. See the License for the specific language governing permissions
and limitations under
* the License.
*/
-package org.apache.geode.redis.springRedisTestApplication;
+package org.apache.geode.redis.session.springRedisTestApplication;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -29,9 +30,13 @@ public class SessionController {
@SuppressWarnings("unchecked")
@GetMapping("/getSessionNotes")
public List<String> getSessionNotes(HttpServletRequest request) {
- List<String> notes =
- (List<String>) request.getSession().getAttribute("NOTES");
- return notes;
+ HttpSession session = request.getSession(false);
+
+ if (session == null) {
+ return null;
+ } else {
+ return (List<String>) session.getAttribute("NOTES");
+ }
}
@SuppressWarnings("unchecked")
@@ -55,6 +60,6 @@ public class SessionController {
@PostMapping("/invalidateSession")
public void invalidateSession(HttpServletRequest request) {
- request.getSession(false).invalidate();
+ request.getSession().invalidate();
}
}
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/DUnitSocketAddressResolver.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/DUnitSocketAddressResolver.java
similarity index 95%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/DUnitSocketAddressResolver.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/DUnitSocketAddressResolver.java
index 5f141d4..baacdc1 100644
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/DUnitSocketAddressResolver.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/DUnitSocketAddressResolver.java
@@ -13,7 +13,7 @@
* the License.
*/
-package org.apache.geode.redis.springRedisTestApplication.config;
+package org.apache.geode.redis.session.springRedisTestApplication.config;
import java.net.InetAddress;
import java.net.InetSocketAddress;
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/SessionListener.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/SessionListener.java
similarity index 71%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/SessionListener.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/SessionListener.java
index 209d43e..519b16e 100644
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/SessionListener.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/SessionListener.java
@@ -12,7 +12,10 @@
* or implied. See the License for the specific language governing permissions
and limitations under
* the License.
*/
-package org.apache.geode.redis.springRedisTestApplication.config;
+package org.apache.geode.redis.session.springRedisTestApplication.config;
+
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@@ -21,15 +24,21 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class SessionListener implements HttpSessionListener {
+ public static AtomicLong sessionCount = new AtomicLong(0);
+ public static HashSet<String> sessionIds = new HashSet<>();
@Override
public void sessionCreated(HttpSessionEvent event) {
+ sessionCount.getAndIncrement();
+ sessionIds.add(event.getSession().getId());
System.out.println("session created: " + event.getSession().getId());
event.getSession().setMaxInactiveInterval(15);
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
- System.out.println("session destroyed " + event.getSession().getId());
+ sessionIds.remove(event.getSession().getId());
+ sessionCount.getAndDecrement();
+ System.out.println("session destroyed: " + event.getSession().getId());
}
}
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/WebMvcConfig.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/WebMvcConfig.java
similarity index 95%
rename from
geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/WebMvcConfig.java
rename to
geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/WebMvcConfig.java
index 5ea5569..909c4e7 100755
---
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/springRedisTestApplication/config/WebMvcConfig.java
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/session/springRedisTestApplication/config/WebMvcConfig.java
@@ -13,7 +13,7 @@
* the License.
*/
-package org.apache.geode.redis.springRedisTestApplication.config;
+package org.apache.geode.redis.session.springRedisTestApplication.config;
import io.lettuce.core.resource.ClientResources;
import org.springframework.boot.ApplicationArguments;
diff --git
a/geode-redis/src/main/java/org/apache/geode/redis/internal/Command.java
b/geode-redis/src/main/java/org/apache/geode/redis/internal/Command.java
index 5b1749c..5dd8fa4 100755
--- a/geode-redis/src/main/java/org/apache/geode/redis/internal/Command.java
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/Command.java
@@ -151,13 +151,26 @@ public class Command {
@Override
public String toString() {
StringBuilder b = new StringBuilder();
- for (byte[] bs : this.commandElems) {
- b.append(Coder.bytesToString(bs));
+ for (byte[] rawCommand : this.commandElems) {
+ b.append(getHexEncodedString(rawCommand));
b.append(' ');
}
return b.toString();
}
+ private String getHexEncodedString(byte[] data) {
+ StringBuilder builder = new StringBuilder();
+ for (byte aByte : data) {
+ if (aByte > 31 && aByte < 127) {
+ builder.append((char) aByte);
+ } else {
+ builder.append(String.format("\\x%02x", aByte));
+ }
+ }
+
+ return builder.toString();
+ }
+
public void execute(ExecutionHandlerContext executionHandlerContext) {
RedisCommandType type = getCommandType();
type.executeCommand(this, executionHandlerContext);
diff --git
a/geode-redis/src/main/java/org/apache/geode/redis/internal/ExecutionHandlerContext.java
b/geode-redis/src/main/java/org/apache/geode/redis/internal/ExecutionHandlerContext.java
index 1ada736..93e8978 100644
---
a/geode-redis/src/main/java/org/apache/geode/redis/internal/ExecutionHandlerContext.java
+++
b/geode-redis/src/main/java/org/apache/geode/redis/internal/ExecutionHandlerContext.java
@@ -144,6 +144,7 @@ public class ExecutionHandlerContext extends
ChannelInboundHandlerAdapter {
if (logger.isDebugEnabled()) {
logger.debug("Executing Redis command: {}", command);
}
+
executeCommand(ctx, command);
} catch (Exception e) {
logger.warn("Execution of Redis command {} failed: {}", command, e);
@@ -215,6 +216,8 @@ public class ExecutionHandlerContext extends
ChannelInboundHandlerAdapter {
executeWithoutTransaction(command);
}
+ logResponse(command);
+
if (hasTransaction() && command.isOfType(RedisCommandType.MULTI)) {
writeToChannel(
Coder.getSimpleStringResponse(this.byteBufAllocator,
RedisConstants.COMMAND_QUEUED));
@@ -240,6 +243,35 @@ public class ExecutionHandlerContext extends
ChannelInboundHandlerAdapter {
}
}
+ private void logResponse(Command command) {
+ if (logger.isDebugEnabled() && command.getResponse() != null) {
+ ByteBuf response = null;
+ try {
+ response = command.getResponse()
+ .copy(0, Math.min(command.getResponse().readableBytes(), 100));
+ logger.debug("Redis command returned: {}",
getPrintableByteBuf(response));
+ } finally {
+ if (response != null) {
+ response.release();
+ }
+ }
+ }
+ }
+
+ private String getPrintableByteBuf(ByteBuf buf) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < buf.readableBytes(); i++) {
+ byte aByte = buf.getByte(i);
+ if (aByte > 31 && aByte < 127) {
+ builder.append((char) aByte);
+ } else {
+ builder.append(String.format("\\x%02x", aByte));
+ }
+ }
+
+ return builder.toString();
+ }
+
/**
* Private helper method to execute a command without a transaction, done
for special exception