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 <jde...@pivotal.io>
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 <mboxw...@vmware.com>
    Co-authored-by: Raymond Ingles <ring...@vmware.com>
    Co-authored-by: Jens Deppe <jde...@vmware.com>
---
 .../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

Reply via email to