You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by br...@apache.org on 2021/04/09 23:28:13 UTC

[geode] 07/12: GEODE-8217: Deserialize attribute before update and remove. (#5256)

This is an automated email from the ASF dual-hosted git repository.

bross pushed a commit to branch support/1.12
in repository https://gitbox.apache.org/repos/asf/geode.git

commit 92821404a57301d7497464a3ded90b9b2123721e
Author: Jacob Barrett <jb...@pivotal.io>
AuthorDate: Thu Jun 25 13:57:17 2020 -0700

    GEODE-8217: Deserialize attribute before update and remove. (#5256)
    
    When preferDeserializedForm is true we deserialize the previous attributes before update or remove.
    
    Deprecates preferDeserializedForm since when false it's unclear when you will get serialized or unserialized forms of attributes.
    
    (cherry picked from commit 9cc61bf2c0cb91f1a6b551fef6984214e23730e2)
---
 .../AbstractDeltaSessionIntegrationTest.java       | 110 +++++++
 .../catalina/AbstractDeltaSessionManagerTest.java  |   4 +-
 .../catalina/AbstractDeltaSessionTest.java}        |  53 ++--
 .../session/catalina/DeltaSession7Test.java}       |  25 +-
 .../session/catalina/DeltaSession7Test.java        | 147 +++++++++
 .../catalina/Tomcat7DeltaSessionManagerTest.java   |   3 +-
 .../session/catalina/DeltaSession8Test.java}       |  25 +-
 .../session/catalina/DeltaSession8Test.java        | 147 +++++++++
 .../catalina/Tomcat8DeltaSessionManagerTest.java   |   3 +-
 .../session/catalina/DeltaSession9Test.java}       |  23 +-
 .../session/catalina/DeltaSession9Test.java        | 147 +++++++++
 .../catalina/Tomcat9DeltaSessionManagerTest.java   |   3 +-
 .../modules/session/catalina/DeltaSession.java     | 332 ++++++++++++---------
 .../session/catalina/DeltaSessionManager.java      | 154 +++++-----
 ....java => DeltaSessionManagerConfiguration.java} |  46 ++-
 .../session/catalina/JvmRouteBinderValve.java      |  10 +-
 .../modules/session/catalina/SessionManager.java   |   4 +
 .../catalina/Tomcat6CommitSessionValve.java        |   3 +-
 .../callback/SessionExpirationCacheListener.java   |   2 +-
 .../session/AccessAttributeValueListener.java}     |  20 +-
 .../src/main/webapp/WEB-INF/web.xml                |   4 +
 .../apache/geode/session/tests/CargoTestBase.java  |  45 ++-
 .../org/apache/geode/session/tests/LogChecker.java | 126 ++++++++
 .../geode/internal/cache/GemFireCacheImpl.java     |   2 +-
 24 files changed, 1146 insertions(+), 292 deletions(-)

diff --git a/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionIntegrationTest.java b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionIntegrationTest.java
new file mode 100644
index 0000000..5008b17
--- /dev/null
+++ b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionIntegrationTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.modules.session.catalina;
+
+import static org.apache.geode.cache.RegionShortcut.PARTITION;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+
+import org.apache.catalina.Context;
+import org.apache.juli.logging.Log;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.internal.util.BlobHelper;
+import org.apache.geode.modules.session.catalina.callback.SessionExpirationCacheListener;
+import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
+import org.apache.geode.test.junit.rules.ServerStarterRule;
+
+public abstract class AbstractDeltaSessionIntegrationTest<DeltaSessionManagerT extends DeltaSessionManager<?>, DeltaSessionT extends DeltaSession> {
+  protected static final String KEY = "key1";
+  protected static final String REGION_NAME = "sessions";
+
+  protected Region<String, HttpSession> region;
+  protected final DeltaSessionManagerT manager;
+  protected final Context context = mock(Context.class);
+
+  @Rule
+  public ServerStarterRule server = new ServerStarterRule().withAutoStart();
+
+  protected AbstractDeltaSessionIntegrationTest(final DeltaSessionManagerT manager) {
+    this.manager = manager;
+  }
+
+  @Before
+  public void before() {
+    region = server.getCache()
+        .<String, HttpSession>createRegionFactory(PARTITION)
+        .addCacheListener(new SessionExpirationCacheListener())
+        .create(REGION_NAME);
+
+    when(manager.getLogger()).thenReturn(mock(Log.class));
+    when(manager.getRegionName()).thenReturn(REGION_NAME);
+    when(manager.getSessionCache()).thenReturn(mock(SessionCache.class));
+    when(manager.getSessionCache().getOperatingRegion()).thenReturn(region);
+    whenGetPreferDeserializedForm(manager);
+
+    final DeltaSessionStatistics stats = mock(DeltaSessionStatistics.class);
+    when(manager.getStatistics()).thenReturn(stats);
+  }
+
+  @SuppressWarnings("deprecation")
+  private void whenGetPreferDeserializedForm(DeltaSessionManager<?> manager) {
+    when(manager.getPreferDeserializedForm()).thenReturn(true);
+  }
+
+  protected abstract DeltaSessionT newSession(final DeltaSessionManagerT manager);
+
+  @Test
+  public void serializedAttributesNotLeakedWhenSessionInvalidated() throws IOException {
+    final HttpSessionAttributeListener listener = mock(HttpSessionAttributeListener.class);
+    when(context.getApplicationEventListeners()).thenReturn(new Object[] {listener});
+
+    final DeltaSessionT session = spy(newSession(manager));
+    session.setId(KEY, false);
+    session.setValid(true);
+    session.setOwner(manager);
+
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    region.put(session.getId(), session);
+
+    session.invalidate();
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+}
diff --git a/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionManagerTest.java b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionManagerTest.java
index ff976b1..d5fea45 100644
--- a/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionManagerTest.java
+++ b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionManagerTest.java
@@ -64,9 +64,9 @@ import org.apache.geode.cache.query.internal.LinkedResultSet;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
 
-public abstract class AbstractDeltaSessionManagerTest {
+public abstract class AbstractDeltaSessionManagerTest<DeltaSessionManagerT extends DeltaSessionManager<?>> {
 
-  protected DeltaSessionManager manager;
+  protected DeltaSessionManagerT manager;
   protected AbstractSessionCache sessionCache;
   protected Cache cache;
   protected Log logger;
diff --git a/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/catalina/DeltaSessionTest.java b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionTest.java
similarity index 82%
rename from extensions/geode-modules/src/test/java/org/apache/geode/modules/session/catalina/DeltaSessionTest.java
rename to extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionTest.java
index fc0c159..eea5639f 100644
--- a/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/catalina/DeltaSessionTest.java
+++ b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/AbstractDeltaSessionTest.java
@@ -46,23 +46,24 @@ import org.apache.geode.internal.util.BlobHelper;
 import org.apache.geode.modules.session.catalina.internal.DeltaSessionAttributeEvent;
 import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
 
-public class DeltaSessionTest {
+public abstract class AbstractDeltaSessionTest<SessionT extends DeltaSession> {
 
-  private final DeltaSessionManager manager = mock(DeltaSessionManager.class);
+  protected final DeltaSessionManager<?> manager = mock(DeltaSessionManager.class);
   private final Region<String, HttpSession> sessionRegion = mock(Region.class);
   private final SessionCache sessionCache = mock(ClientServerSessionCache.class);
-  DeltaSessionStatistics stats = mock(DeltaSessionStatistics.class);
-  private final String sessionRegionName = "sessionRegionName";
-  private final String contextName = "contextName";
+  private final DeltaSessionStatistics stats = mock(DeltaSessionStatistics.class);
   private final Log logger = mock(Log.class);
 
   @Before
   public void setup() {
+    String sessionRegionName = "sessionRegionName";
     when(manager.getRegionName()).thenReturn(sessionRegionName);
     when(manager.getSessionCache()).thenReturn(sessionCache);
     when(manager.getLogger()).thenReturn(logger);
-    when(manager.getContextName()).thenReturn(contextName);
+    when(manager.getContextName()).thenReturn("contextName");
     when(manager.getStatistics()).thenReturn(stats);
+    when(manager.isBackingCacheAvailable()).thenReturn(true);
+    setupDeprecated();
     // For Client/Server behavior and some PeerToPeer use cases the session region and operating
     // regions
     // will be the same.
@@ -70,25 +71,31 @@ public class DeltaSessionTest {
     when(logger.isDebugEnabled()).thenReturn(true);
   }
 
+  @SuppressWarnings("deprecation")
+  protected void setupDeprecated() {
+    when(manager.getPreferDeserializedForm()).thenReturn(true);
+  }
+
+  protected abstract SessionT newDeltaSession(Manager manager);
+
   @Test
   public void sessionConstructionThrowsIllegalArgumentExceptionIfProvidedManagerIsNotDeltaSessionManager() {
     final Manager invalidManager = mock(Manager.class);
 
-    assertThatThrownBy(() -> new DeltaSession(invalidManager))
+    assertThatThrownBy(() -> newDeltaSession(invalidManager))
         .isInstanceOf(IllegalArgumentException.class)
         .hasMessageContaining("The Manager must be an AbstractManager");
   }
 
   @Test
   public void sessionConstructionDoesNotThrowExceptionWithValidArgument() {
-    final DeltaSession session = new DeltaSession(manager);
-
+    newDeltaSession(manager);
     verify(logger).debug(anyString());
   }
 
   @Test
   public void getSessionCreatesFacadeWhenFacadeIsNullAndPackageProtectionDisabled() {
-    final DeltaSession session = new DeltaSession(manager);
+    final DeltaSession session = newDeltaSession(manager);
 
     final HttpSession returnedSession = session.getSession();
 
@@ -97,7 +104,7 @@ public class DeltaSessionTest {
 
   @Test
   public void getSessionCreatesFacadeWhenFacadeIsNullAndPackageProtectionEnabled() {
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
     final DeltaSessionFacade facade = mock(DeltaSessionFacade.class);
     doReturn(true).when(session).isPackageProtectionEnabled();
     doReturn(facade).when(session).getNewFacade(any(DeltaSession.class));
@@ -109,7 +116,7 @@ public class DeltaSessionTest {
 
   @Test
   public void processExpiredIncrementsStatisticsCountForExpiredSessions() {
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
 
     doNothing().when((StandardSession) session).expire(false);
     session.processExpired();
@@ -124,8 +131,9 @@ public class DeltaSessionTest {
     final List<DeltaSessionAttributeEvent> events = new ArrayList<>();
     events.add(event1);
     events.add(event2);
+    @SuppressWarnings("unchecked")
     final Region<String, DeltaSessionInterface> region = mock(Region.class);
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
 
     session.applyAttributeEvents(region, events);
 
@@ -139,11 +147,11 @@ public class DeltaSessionTest {
 
   @Test
   public void commitThrowsIllegalStateExceptionWhenCalledOnInvalidSession() {
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
     final String sessionId = "invalidatedSession";
     doReturn(sessionId).when(session).getId();
 
-    assertThatThrownBy(() -> session.commit()).isInstanceOf(IllegalStateException.class)
+    assertThatThrownBy(session::commit).isInstanceOf(IllegalStateException.class)
         .hasMessage("commit: Session " + sessionId + " already invalidated");
   }
 
@@ -151,7 +159,7 @@ public class DeltaSessionTest {
   public void getSizeInBytesReturnsProperValueForMultipleAttributes() {
     final String attrName1 = "attrName1";
     final String attrName2 = "attrName2";
-    final List attrList = new ArrayList<String>();
+    final List<String> attrList = new ArrayList<>();
     attrList.add(attrName1);
     attrList.add(attrName2);
 
@@ -161,7 +169,7 @@ public class DeltaSessionTest {
     final byte[] value2 = {0, 0, 0, 0, 0};
     final int totalSize = value1.length + value2.length;
 
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
     doReturn(attrNames).when(session).getAttributeNames();
     doReturn(value1).when(session).getAttributeWithoutDeserialize(attrName1);
     doReturn(value2).when(session).getAttributeWithoutDeserialize(attrName2);
@@ -177,7 +185,7 @@ public class DeltaSessionTest {
     final String exceptionMessaage = "Serialization failed.";
     final IOException exception = new IOException(exceptionMessaage);
 
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
     doThrow(exception).when(session).serializeViaBlobHelper(obj);
     session.serialize(obj);
 
@@ -189,17 +197,10 @@ public class DeltaSessionTest {
     final Object obj = "unserialized object";
     final byte[] serializedObj = BlobHelper.serializeToBlob(obj);
 
-    final DeltaSession session = spy(new DeltaSession(manager));
+    final DeltaSession session = spy(newDeltaSession(manager));
     final byte[] result = session.serialize(obj);
 
     assertThat(result).isEqualTo(serializedObj);
   }
 
-  // @Test
-  // public void testToData() throws IOException {
-  // DeltaSession session = spy(new DeltaSession(manager));
-  // DataOutput out = mock(DataOutput.class);
-  //
-  // session.toData(out);
-  // }
 }
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java b/extensions/geode-modules-tomcat7/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java
similarity index 62%
copy from extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
copy to extensions/geode-modules-tomcat7/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java
index b27fe65..7af5a91 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
+++ b/extensions/geode-modules-tomcat7/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java
@@ -12,16 +12,29 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.apache.geode.modules.session.catalina;
 
-import org.apache.catalina.connector.Response;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
 
-@Deprecated
-public final class Tomcat6CommitSessionValve extends AbstractCommitSessionValve {
+public class DeltaSession7Test
+    extends AbstractDeltaSessionIntegrationTest<Tomcat7DeltaSessionManager, DeltaSession7> {
+
+  public DeltaSession7Test() {
+    super(mock(Tomcat7DeltaSessionManager.class));
+  }
 
   @Override
-  protected Response wrapResponse(Response response) {
-    return response;
+  public void before() {
+    super.before();
+    when(manager.getContainer()).thenReturn(context);
   }
+
+  @Override
+  protected DeltaSession7 newSession(Tomcat7DeltaSessionManager manager) {
+    return new DeltaSession7(manager);
+  }
+
 }
diff --git a/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java b/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java
new file mode 100644
index 0000000..dd53c9c
--- /dev/null
+++ b/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession7Test.java
@@ -0,0 +1,147 @@
+/*
+ * 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.modules.session.catalina;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.juli.logging.Log;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import org.apache.geode.internal.util.BlobHelper;
+
+public class DeltaSession7Test extends AbstractDeltaSessionTest<DeltaSession7> {
+  final HttpSessionAttributeListener listener = mock(HttpSessionAttributeListener.class);
+
+  @Before
+  @Override
+  public void setup() {
+    super.setup();
+
+    final Context context = mock(Context.class);
+    when(manager.getContainer()).thenReturn(context);
+    when(context.getApplicationEventListeners()).thenReturn(new Object[] {listener});
+    when(context.getLogger()).thenReturn(mock(Log.class));
+  }
+
+  @Override
+  protected DeltaSession7 newDeltaSession(Manager manager) {
+    return new DeltaSession7(manager);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeReplaceEvent() throws IOException {
+    final DeltaSession7 session = spy(new DeltaSession7(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeRemovedEvent() throws IOException {
+    final DeltaSession7 session = spy(new DeltaSession7(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeReplaceEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession7 session = spy(new DeltaSession7(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeRemovedEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession7 session = spy(new DeltaSession7(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @SuppressWarnings("deprecation")
+  protected void setPreferDeserializedFormFalse() {
+    when(manager.getPreferDeserializedForm()).thenReturn(false);
+  }
+
+}
diff --git a/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/Tomcat7DeltaSessionManagerTest.java b/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/Tomcat7DeltaSessionManagerTest.java
index e6fd534..20983fc 100644
--- a/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/Tomcat7DeltaSessionManagerTest.java
+++ b/extensions/geode-modules-tomcat7/src/test/java/org/apache/geode/modules/session/catalina/Tomcat7DeltaSessionManagerTest.java
@@ -34,7 +34,8 @@ import org.junit.Test;
 
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 
-public class Tomcat7DeltaSessionManagerTest extends AbstractDeltaSessionManagerTest {
+public class Tomcat7DeltaSessionManagerTest
+    extends AbstractDeltaSessionManagerTest<Tomcat7DeltaSessionManager> {
   private Pipeline pipeline;
 
   @Before
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java b/extensions/geode-modules-tomcat8/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java
similarity index 62%
copy from extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
copy to extensions/geode-modules-tomcat8/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java
index b27fe65..dd8c705 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
+++ b/extensions/geode-modules-tomcat8/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java
@@ -12,16 +12,29 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.apache.geode.modules.session.catalina;
 
-import org.apache.catalina.connector.Response;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
 
-@Deprecated
-public final class Tomcat6CommitSessionValve extends AbstractCommitSessionValve {
+public class DeltaSession8Test
+    extends AbstractDeltaSessionIntegrationTest<Tomcat8DeltaSessionManager, DeltaSession8> {
+
+  public DeltaSession8Test() {
+    super(mock(Tomcat8DeltaSessionManager.class));
+  }
 
   @Override
-  protected Response wrapResponse(Response response) {
-    return response;
+  public void before() {
+    super.before();
+    when(manager.getContext()).thenReturn(context);
   }
+
+  @Override
+  protected DeltaSession8 newSession(Tomcat8DeltaSessionManager manager) {
+    return new DeltaSession8(manager);
+  }
+
 }
diff --git a/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java b/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java
new file mode 100644
index 0000000..d85dd74
--- /dev/null
+++ b/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession8Test.java
@@ -0,0 +1,147 @@
+/*
+ * 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.modules.session.catalina;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.juli.logging.Log;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import org.apache.geode.internal.util.BlobHelper;
+
+public class DeltaSession8Test extends AbstractDeltaSessionTest<DeltaSession8> {
+  final HttpSessionAttributeListener listener = mock(HttpSessionAttributeListener.class);
+
+  @Before
+  @Override
+  public void setup() {
+    super.setup();
+
+    final Context context = mock(Context.class);
+    when(manager.getContext()).thenReturn(context);
+    when(context.getApplicationEventListeners()).thenReturn(new Object[] {listener});
+    when(context.getLogger()).thenReturn(mock(Log.class));
+  }
+
+  @Override
+  protected DeltaSession8 newDeltaSession(Manager manager) {
+    return new DeltaSession8(manager);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeReplaceEvent() throws IOException {
+    final DeltaSession8 session = spy(new DeltaSession8(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeRemovedEvent() throws IOException {
+    final DeltaSession8 session = spy(new DeltaSession8(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeReplaceEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession8 session = spy(new DeltaSession8(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeRemovedEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession8 session = spy(new DeltaSession8(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @SuppressWarnings("deprecation")
+  protected void setPreferDeserializedFormFalse() {
+    when(manager.getPreferDeserializedForm()).thenReturn(false);
+  }
+
+}
diff --git a/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManagerTest.java b/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManagerTest.java
index c293be3..ccb2e1e 100644
--- a/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManagerTest.java
+++ b/extensions/geode-modules-tomcat8/src/test/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManagerTest.java
@@ -33,7 +33,8 @@ import org.junit.Test;
 
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 
-public class Tomcat8DeltaSessionManagerTest extends AbstractDeltaSessionManagerTest {
+public class Tomcat8DeltaSessionManagerTest
+    extends AbstractDeltaSessionManagerTest<Tomcat8DeltaSessionManager> {
   private Pipeline pipeline;
 
   @Before
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java b/extensions/geode-modules-tomcat9/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java
similarity index 62%
copy from extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
copy to extensions/geode-modules-tomcat9/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java
index b27fe65..34d4716 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
+++ b/extensions/geode-modules-tomcat9/src/integrationTest/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java
@@ -12,16 +12,27 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.apache.geode.modules.session.catalina;
 
-import org.apache.catalina.connector.Response;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DeltaSession9Test
+    extends AbstractDeltaSessionIntegrationTest<Tomcat9DeltaSessionManager, DeltaSession9> {
 
-@Deprecated
-public final class Tomcat6CommitSessionValve extends AbstractCommitSessionValve {
+  public DeltaSession9Test() {
+    super(mock(Tomcat9DeltaSessionManager.class));
+  }
 
   @Override
-  protected Response wrapResponse(Response response) {
-    return response;
+  public void before() {
+    super.before();
+    when(manager.getContext()).thenReturn(context);
   }
+
+  @Override
+  protected DeltaSession9 newSession(Tomcat9DeltaSessionManager manager) {
+    return new DeltaSession9(manager);
+  }
+
 }
diff --git a/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java b/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java
new file mode 100644
index 0000000..94b2ef5
--- /dev/null
+++ b/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/DeltaSession9Test.java
@@ -0,0 +1,147 @@
+/*
+ * 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.modules.session.catalina;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.juli.logging.Log;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import org.apache.geode.internal.util.BlobHelper;
+
+public class DeltaSession9Test extends AbstractDeltaSessionTest<DeltaSession9> {
+  final HttpSessionAttributeListener listener = mock(HttpSessionAttributeListener.class);
+
+  @Before
+  @Override
+  public void setup() {
+    super.setup();
+
+    final Context context = mock(Context.class);
+    when(manager.getContext()).thenReturn(context);
+    when(context.getApplicationEventListeners()).thenReturn(new Object[] {listener});
+    when(context.getLogger()).thenReturn(mock(Log.class));
+  }
+
+  @Override
+  protected DeltaSession9 newDeltaSession(Manager manager) {
+    return new DeltaSession9(manager);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeReplaceEvent() throws IOException {
+    final DeltaSession9 session = spy(new DeltaSession9(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesNotLeakedInAttributeRemovedEvent() throws IOException {
+    final DeltaSession9 session = spy(new DeltaSession9(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isEqualTo(value1);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeReplaceEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession9 session = spy(new DeltaSession9(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    final Object value2 = "value2";
+    session.setAttribute(name, value2);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeReplaced(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @Test
+  public void serializedAttributesLeakedInAttributeRemovedEventWhenPreferDeserializedFormFalse()
+      throws IOException {
+    setPreferDeserializedFormFalse();
+
+    final DeltaSession9 session = spy(new DeltaSession9(manager));
+    session.setValid(true);
+    final String name = "attribute";
+    final Object value1 = "value1";
+    final byte[] serializedValue1 = BlobHelper.serializeToBlob(value1);
+    // simulates initial deserialized state with serialized attribute values.
+    session.getAttributes().put(name, serializedValue1);
+
+    session.removeAttribute(name);
+
+    final ArgumentCaptor<HttpSessionBindingEvent> event =
+        ArgumentCaptor.forClass(HttpSessionBindingEvent.class);
+    verify(listener).attributeRemoved(event.capture());
+    verifyNoMoreInteractions(listener);
+    assertThat(event.getValue().getValue()).isInstanceOf(byte[].class);
+  }
+
+  @SuppressWarnings("deprecation")
+  protected void setPreferDeserializedFormFalse() {
+    when(manager.getPreferDeserializedForm()).thenReturn(false);
+  }
+
+}
diff --git a/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/Tomcat9DeltaSessionManagerTest.java b/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/Tomcat9DeltaSessionManagerTest.java
index 5cdeb058..728b68d 100644
--- a/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/Tomcat9DeltaSessionManagerTest.java
+++ b/extensions/geode-modules-tomcat9/src/test/java/org/apache/geode/modules/session/catalina/Tomcat9DeltaSessionManagerTest.java
@@ -33,7 +33,8 @@ import org.junit.Test;
 
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 
-public class Tomcat9DeltaSessionManagerTest extends AbstractDeltaSessionManagerTest {
+public class Tomcat9DeltaSessionManagerTest
+    extends AbstractDeltaSessionManagerTest<Tomcat9DeltaSessionManager> {
   private Pipeline pipeline;
 
   @Before
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession.java
index 4e5e969..9a1e98d 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.modules.session.catalina;
 
+
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
@@ -36,6 +37,7 @@ import org.apache.catalina.ha.session.SerializablePrincipal;
 import org.apache.catalina.realm.GenericPrincipal;
 import org.apache.catalina.security.SecurityUtil;
 import org.apache.catalina.session.StandardSession;
+import org.apache.juli.logging.Log;
 
 import org.apache.geode.DataSerializable;
 import org.apache.geode.DataSerializer;
@@ -69,17 +71,21 @@ public class DeltaSession extends StandardSession
 
   private final transient Object changeLock = new Object();
 
-  private final List<DeltaSessionAttributeEvent> eventQueue = new ArrayList<>();
+  private final ArrayList<DeltaSessionAttributeEvent> eventQueue = new ArrayList<>();
 
   private transient GatewayDeltaEvent currentGatewayDeltaEvent;
 
   private transient boolean expired = false;
 
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
   private transient boolean preferDeserializedForm = true;
 
   private byte[] serializedPrincipal;
 
-  private static Field cachedField;
+  private static final Field cachedField;
 
   static {
     try {
@@ -112,7 +118,6 @@ public class DeltaSession extends StandardSession
    * Return the <code>HttpSession</code> for which this object is the facade.
    */
   @Override
-  @SuppressWarnings("unchecked")
   public HttpSession getSession() {
     if (facade == null) {
       if (isPackageProtectionEnabled()) {
@@ -127,27 +132,29 @@ public class DeltaSession extends StandardSession
 
   @Override
   public Principal getPrincipal() {
-    if (this.principal == null && this.serializedPrincipal != null) {
-      SerializablePrincipal sp;
+    final DeltaSessionManager<?> deltaSessionManager = getDeltaSessionManager();
+
+    if (principal == null && serializedPrincipal != null) {
+      final Log logger = deltaSessionManager.getLogger();
+
+      final SerializablePrincipal sp;
       try {
-        sp = (SerializablePrincipal) BlobHelper.deserializeBlob(this.serializedPrincipal);
+        sp = (SerializablePrincipal) BlobHelper.deserializeBlob(serializedPrincipal);
       } catch (Exception e) {
-        String builder = this
-            + ": Serialized principal contains a byte[] that cannot be deserialized due to the following exception";
-        ((DeltaSessionManager) getManager()).getLogger().warn(builder, e);
+        logger.warn(this
+            + ": Serialized principal contains a byte[] that cannot be deserialized due to the following exception",
+            e);
         return null;
       }
-      this.principal =
-          sp.getPrincipal(((DeltaSessionManager) this.manager).getTheContext().getRealm());
-      if (getManager() != null) {
-        DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-        if (mgr.getLogger().isDebugEnabled()) {
-          mgr.getLogger().debug(this + ": Deserialized principal: " + this.principal);
-          // mgr.logCurrentStack();
-        }
+
+      principal = sp.getPrincipal(deltaSessionManager.getTheContext().getRealm());
+
+      if (logger.isDebugEnabled()) {
+        logger.debug(this + ": Deserialized principal: " + principal);
       }
     }
-    return this.principal;
+
+    return principal;
   }
 
   @Override
@@ -158,46 +165,43 @@ public class DeltaSession extends StandardSession
     if (getManager() != null) {
       // TODO convert this to a delta
       getManager().add(this);
-      DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-      if (mgr.getLogger().isDebugEnabled()) {
-        mgr.getLogger().debug(this + ": Cached principal: " + principal);
-        // mgr.logCurrentStack();
+      final Log logger = getDeltaSessionManager().getLogger();
+      if (logger.isDebugEnabled()) {
+        logger.debug(this + ": Cached principal: " + principal);
       }
     }
   }
 
   private byte[] getSerializedPrincipal() {
-    if (this.serializedPrincipal == null) {
-      if (this.principal != null && this.principal instanceof GenericPrincipal) {
-        GenericPrincipal gp = (GenericPrincipal) this.principal;
+    if (serializedPrincipal == null) {
+      if (principal != null && principal instanceof GenericPrincipal) {
+        GenericPrincipal gp = (GenericPrincipal) principal;
         SerializablePrincipal sp = SerializablePrincipal.createPrincipal(gp);
-        this.serializedPrincipal = serialize(sp);
+        serializedPrincipal = serialize(sp);
         if (manager != null) {
-          DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-          if (mgr.getLogger().isDebugEnabled()) {
-            mgr.getLogger().debug(this + ": Serialized principal: " + sp);
-            // mgr.logCurrentStack();
+          final Log logger = getDeltaSessionManager().getLogger();
+          if (logger.isDebugEnabled()) {
+            logger.debug(this + ": Serialized principal: " + sp);
           }
         }
       }
     }
-    return this.serializedPrincipal;
+    return serializedPrincipal;
   }
 
   private Region<String, HttpSession> getOperatingRegion() {
     // This region shouldn't be null when it is needed.
     // It should have been set by the setOwner method.
-    return this.operatingRegion;
+    return operatingRegion;
   }
 
   boolean isCommitEnabled() {
-    DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-    return mgr.isCommitValveEnabled();
+    return getDeltaSessionManager().isCommitValveEnabled();
   }
 
   @Override
   public GatewayDeltaEvent getCurrentGatewayDeltaEvent() {
-    return this.currentGatewayDeltaEvent;
+    return currentGatewayDeltaEvent;
   }
 
   @Override
@@ -208,29 +212,34 @@ public class DeltaSession extends StandardSession
   @Override
   public void setOwner(Object manager) {
     if (manager instanceof DeltaSessionManager) {
-      DeltaSessionManager sessionManager = (DeltaSessionManager) manager;
+      DeltaSessionManager<?> sessionManager = (DeltaSessionManager<?>) manager;
       this.manager = sessionManager;
       initializeRegion(sessionManager);
-      this.hasDelta = false;
-      this.applyRemotely = false;
-      this.enableGatewayDeltaReplication = sessionManager.getEnableGatewayDeltaReplication();
-      this.preferDeserializedForm = sessionManager.getPreferDeserializedForm();
+      hasDelta = false;
+      applyRemotely = false;
+      enableGatewayDeltaReplication = sessionManager.getEnableGatewayDeltaReplication();
+      setOwnerDeprecated(sessionManager);
 
       // Initialize transient variables
-      if (this.listeners == null) {
-        this.listeners = new ArrayList();
+      if (listeners == null) {
+        listeners = new ArrayList<>();
       }
 
-      if (this.notes == null) {
-        this.notes = new Hashtable();
+      if (notes == null) {
+        notes = new Hashtable<>();
       }
 
-      contextName = ((DeltaSessionManager) manager).getContextName();
+      contextName = sessionManager.getContextName();
     } else {
       throw new IllegalArgumentException(this + ": The Manager must be an AbstractManager");
     }
   }
 
+  @SuppressWarnings("deprecation")
+  private void setOwnerDeprecated(DeltaSessionManager<?> sessionManager) {
+    preferDeserializedForm = sessionManager.getPreferDeserializedForm();
+  }
+
   private void checkBackingCacheAvailable() {
     if (!((SessionManager) getManager()).isBackingCacheAvailable()) {
       throw new IllegalStateException("No backing cache server is available.");
@@ -242,12 +251,15 @@ public class DeltaSession extends StandardSession
 
     checkBackingCacheAvailable();
 
-    synchronized (this.changeLock) {
+    synchronized (changeLock) {
       // Serialize the value
       byte[] serializedValue = serialize(value);
 
       // Store the attribute locally
-      if (this.preferDeserializedForm) {
+      if (preferDeserializedForm) {
+        if (notify) {
+          deserializeAttributeInternal(name);
+        }
         super.setAttribute(name, value, true);
       } else {
         super.setAttribute(name, serializedValue, true);
@@ -262,7 +274,6 @@ public class DeltaSession extends StandardSession
           new DeltaSessionUpdateAttributeEvent(name, serializedValue);
       queueAttributeEvent(event, true);
 
-
       // Distribute the update
       if (!isCommitEnabled()) {
         putInRegion(getOperatingRegion(), true, null);
@@ -276,9 +287,13 @@ public class DeltaSession extends StandardSession
     if (expired) {
       return;
     }
-    synchronized (this.changeLock) {
+    synchronized (changeLock) {
+      if (notify && preferDeserializedForm) {
+        deserializeAttributeInternal(name);
+      }
+
       // Remove the attribute locally
-      super.removeAttribute(name, true);
+      super.removeAttribute(name, notify);
 
       // Create the destroy attribute message
       DeltaSessionAttributeEvent event = new DeltaSessionDestroyAttributeEvent(name);
@@ -292,34 +307,70 @@ public class DeltaSession extends StandardSession
   }
 
   @Override
+  protected void removeAttributeInternal(String name, boolean notify) {
+    if (notify && preferDeserializedForm) {
+      deserializeAttributeInternal(name);
+    }
+
+    super.removeAttributeInternal(name, notify);
+  }
+
+  protected Object getAttributeInternal(final String name) {
+    if (null == name) {
+      return null;
+    }
+    return getAttributes().get(name);
+  }
+
+  protected void setAttributeInternal(String name, Object value) {
+    if (null == name) {
+      return;
+    }
+    getAttributes().put(name, value);
+  }
+
+  @Override
   public Object getAttribute(String name) {
     checkBackingCacheAvailable();
-    Object value = super.getAttribute(name);
+    Object value = deserializeAttribute(name, super.getAttribute(name), preferDeserializedForm);
+
+    // Touch the session region if necessary. This is an asynchronous operation
+    // that prevents the session region from prematurely expiring a session that
+    // is only getting attributes.
+    getDeltaSessionManager().addSessionToTouch(getId());
+
+    return value;
+  }
+
+  protected void deserializeAttributeInternal(final String name) {
+    deserializeAttribute(name, getAttributeInternal(name), true);
+  }
 
+  private Object deserializeAttribute(final String name, final Object value, final boolean store) {
     // If the attribute is a byte[] (meaning it came from the server),
     // deserialize it and add it to attributes map before returning it.
     if (value instanceof byte[]) {
       try {
-        value = BlobHelper.deserializeBlob((byte[]) value);
-      } catch (Exception e) {
-        String builder = this + ": Attribute named " + name
-            + " contains a byte[] that cannot be deserialized due to the following exception";
-        ((DeltaSessionManager) getManager()).getLogger().warn(
-            builder, e);
-      }
-      if (this.preferDeserializedForm) {
-        localUpdateAttribute(name, value);
+        final Object deserialized = BlobHelper.deserializeBlob((byte[]) value);
+        if (store) {
+          setAttributeInternal(name, deserialized);
+        }
+        return deserialized;
+      } catch (final Exception e) {
+        getDeltaSessionManager().getLogger().warn(
+            this + ": Attribute named " + name
+                + " contains a byte[] that cannot be deserialized due to the following exception",
+            e);
       }
     }
 
-    // Touch the session region if necessary. This is an asynchronous operation
-    // that prevents the session region from prematurely expiring a session that
-    // is only getting attributes.
-    ((DeltaSessionManager) getManager()).addSessionToTouch(getId());
-
     return value;
   }
 
+  private DeltaSessionManager<?> getDeltaSessionManager() {
+    return (DeltaSessionManager<?>) getManager();
+  }
+
   Object getAttributeWithoutDeserialize(String name) {
     return super.getAttribute(name);
   }
@@ -327,15 +378,14 @@ public class DeltaSession extends StandardSession
   @Override
   public void invalidate() {
     super.invalidate();
-    // getOperatingRegion().destroy(this.id, true); // already done in super (remove)
-    ((DeltaSessionManager) getManager()).getStatistics().incSessionsInvalidated();
+    getDeltaSessionManager().getStatistics().incSessionsInvalidated();
   }
 
   @Override
   public void processExpired() {
-    DeltaSessionManager manager = (DeltaSessionManager) getManager();
+    DeltaSessionManager<?> manager = getDeltaSessionManager();
     if (manager != null && manager.getLogger() != null && manager.getLogger().isDebugEnabled()) {
-      ((DeltaSessionManager) getManager()).getLogger().debug(this + ": Expired");
+      getDeltaSessionManager().getLogger().debug(this + ": Expired");
     }
 
     // Set expired (so region.destroy is not called again)
@@ -353,7 +403,7 @@ public class DeltaSession extends StandardSession
   @Override
   public void expire(boolean notify) {
     if (notify) {
-      getOperatingRegion().destroy(this.getId(), this);
+      getOperatingRegion().destroy(getId(), this);
     } else {
       super.expire(false);
     }
@@ -366,7 +416,7 @@ public class DeltaSession extends StandardSession
 
   @Override
   public void localUpdateAttribute(String name, Object value) {
-    if (this.manager == null) {
+    if (manager == null) {
       // Name cannot be null
       if (name == null) {
         throw new IllegalArgumentException(sm.getString("standardSession.setAttribute.namenull"));
@@ -387,7 +437,8 @@ public class DeltaSession extends StandardSession
       // Replace or add this attribute
       getAttributes().put(name, value);
     } else {
-      super.setAttribute(name, value, false); // don't do notification since this is a replication
+      // don't do notification since this is a replication
+      super.setAttribute(name, value, false);
     }
   }
 
@@ -407,18 +458,18 @@ public class DeltaSession extends StandardSession
     putInRegion(region, false, true);
   }
 
-  private void initializeRegion(DeltaSessionManager sessionManager) {
+  private void initializeRegion(DeltaSessionManager<?> sessionManager) {
     // Get the session region name
-    this.sessionRegionName = sessionManager.getRegionName();
+    sessionRegionName = sessionManager.getRegionName();
 
     // Get the operating region.
     // If a P2P manager is used, then this will be a local region fronting the
     // session region if local cache is enabled; otherwise, it will be the
     // session region itself.
     // If a CS manager is used, it will be the session proxy region.
-    this.operatingRegion = sessionManager.getSessionCache().getOperatingRegion();
+    operatingRegion = sessionManager.getSessionCache().getOperatingRegion();
     if (sessionManager.getLogger().isDebugEnabled()) {
-      sessionManager.getLogger().debug(this + ": Set operating region: " + this.operatingRegion);
+      sessionManager.getLogger().debug(this + ": Set operating region: " + operatingRegion);
     }
   }
 
@@ -429,56 +480,55 @@ public class DeltaSession extends StandardSession
       // If the manager has enabled gateway delta replication and is a P2P
       // manager, the GatewayDeltaForwardCacheListener will be invoked in this
       // VM. Add the event to the currentDelta.
-      DeltaSessionManager mgr = (DeltaSessionManager) this.manager;
-      if (this.enableGatewayDeltaReplication && mgr.isPeerToPeer()) {
+      if (enableGatewayDeltaReplication && getDeltaSessionManager().isPeerToPeer()) {
         // If commit is not enabled, add the event to the current batch; else,
         // the current batch will be initialized to the events in the queue will
         // be added at commit time.
         if (!isCommitEnabled()) {
           List<DeltaSessionAttributeEvent> events = new ArrayList<>();
           events.add(event);
-          this.currentGatewayDeltaEvent =
-              new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events);
+          currentGatewayDeltaEvent =
+              new DeltaSessionAttributeEventBatch(sessionRegionName, id, events);
         }
       }
     }
     addEventToEventQueue(event);
   }
 
-  @SuppressWarnings("unchecked")
-  private void putInRegion(Region region, boolean applyRemotely, Object callbackArgument) {
-    this.hasDelta = true;
+  private void putInRegion(Region region, boolean applyRemotely,
+      Object callbackArgument) {
+    hasDelta = true;
     this.applyRemotely = applyRemotely;
-    region.put(this.id, this, callbackArgument);
-    this.eventQueue.clear();
+    region.put(id, this, callbackArgument);
+    eventQueue.clear();
   }
 
   @Override
   public void commit() {
-    if (!isValidInternal())
+    if (!isValidInternal()) {
       throw new IllegalStateException("commit: Session " + getId() + " already invalidated");
+    }
     // (STRING_MANAGER.getString("deltaSession.commit.ise", getId()));
 
-    synchronized (this.changeLock) {
+    synchronized (changeLock) {
       // Jens - there used to be a check to only perform this if the queue is
       // empty, but we want this to always run so that the lastAccessedTime
       // will be updated even when no attributes have been changed.
-      DeltaSessionManager mgr = (DeltaSessionManager) this.manager;
-      if (this.enableGatewayDeltaReplication && mgr.isPeerToPeer()) {
+      if (enableGatewayDeltaReplication && getDeltaSessionManager().isPeerToPeer()) {
         setCurrentGatewayDeltaEvent(
-            new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, this.eventQueue));
+            new DeltaSessionAttributeEventBatch(sessionRegionName, id, eventQueue));
       }
-      this.hasDelta = true;
-      this.applyRemotely = true;
+      hasDelta = true;
+      applyRemotely = true;
       putInRegion(getOperatingRegion(), true, null);
-      this.eventQueue.clear();
+      eventQueue.clear();
     }
   }
 
   @Override
   public void abort() {
-    synchronized (this.changeLock) {
-      this.eventQueue.clear();
+    synchronized (changeLock) {
+      eventQueue.clear();
     }
   }
 
@@ -488,7 +538,7 @@ public class DeltaSession extends StandardSession
 
   @Override
   public boolean getExpired() {
-    return this.expired;
+    return expired;
   }
 
   @Override
@@ -498,25 +548,25 @@ public class DeltaSession extends StandardSession
 
   @Override
   public boolean hasDelta() {
-    return this.hasDelta;
+    return hasDelta;
   }
 
   @Override
   public void toDelta(DataOutput out) throws IOException {
     // Write whether to apply the changes to another DS if necessary
-    out.writeBoolean(this.applyRemotely);
+    out.writeBoolean(applyRemotely);
 
     // Write the events
-    DataSerializer.writeArrayList((ArrayList) this.eventQueue, out);
+    DataSerializer.writeArrayList(eventQueue, out);
 
-    out.writeLong(this.lastAccessedTime);
-    out.writeInt(this.maxInactiveInterval);
+    out.writeLong(lastAccessedTime);
+    out.writeInt(maxInactiveInterval);
   }
 
   @Override
   public void fromDelta(DataInput in) throws IOException, InvalidDeltaException {
     // Read whether to apply the changes to another DS if necessary
-    this.applyRemotely = in.readBoolean();
+    applyRemotely = in.readBoolean();
 
     // Read the events
     List<DeltaSessionAttributeEvent> events;
@@ -528,8 +578,8 @@ public class DeltaSession extends StandardSession
 
     // This allows for backwards compatibility with 2.1 clients
     if (((InputStream) in).available() > 0) {
-      this.lastAccessedTime = in.readLong();
-      this.maxInactiveInterval = in.readInt();
+      lastAccessedTime = in.readLong();
+      maxInactiveInterval = in.readInt();
     }
 
     // Iterate and apply the events
@@ -538,9 +588,9 @@ public class DeltaSession extends StandardSession
     }
 
     // Add the events to the gateway delta region if necessary
-    if (this.enableGatewayDeltaReplication && this.applyRemotely) {
+    if (enableGatewayDeltaReplication && applyRemotely) {
       setCurrentGatewayDeltaEvent(
-          new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events));
+          new DeltaSessionAttributeEventBatch(sessionRegionName, id, events));
     }
 
     // Access it to set the last accessed time. End access it to set not new.
@@ -551,57 +601,57 @@ public class DeltaSession extends StandardSession
   @Override
   public void toData(DataOutput out) throws IOException {
     // Write the StandardSession state
-    DataSerializer.writeString(this.id, out);
-    out.writeLong(this.creationTime);
-    out.writeLong(this.lastAccessedTime);
-    out.writeLong(this.thisAccessedTime);
-    out.writeInt(this.maxInactiveInterval);
-    out.writeBoolean(this.isNew);
-    out.writeBoolean(this.isValid);
+    DataSerializer.writeString(id, out);
+    out.writeLong(creationTime);
+    out.writeLong(lastAccessedTime);
+    out.writeLong(thisAccessedTime);
+    out.writeInt(maxInactiveInterval);
+    out.writeBoolean(isNew);
+    out.writeBoolean(isValid);
     DataSerializer.writeObject(getSerializedAttributes(), out);
     DataSerializer.writeByteArray(getSerializedPrincipal(), out);
 
     // Write the DeltaSession state
-    out.writeBoolean(this.enableGatewayDeltaReplication);
-    DataSerializer.writeString(this.sessionRegionName, out);
+    out.writeBoolean(enableGatewayDeltaReplication);
+    DataSerializer.writeString(sessionRegionName, out);
 
-    DataSerializer.writeString(this.contextName, out);
+    DataSerializer.writeString(contextName, out);
   }
 
   @Override
   public void fromData(DataInput in) throws IOException, ClassNotFoundException {
     // Read the StandardSession state
-    this.id = DataSerializer.readString(in);
-    this.creationTime = in.readLong();
-    this.lastAccessedTime = in.readLong();
-    this.thisAccessedTime = in.readLong();
-    this.maxInactiveInterval = in.readInt();
-    this.isNew = in.readBoolean();
-    this.isValid = in.readBoolean();
+    id = DataSerializer.readString(in);
+    creationTime = in.readLong();
+    lastAccessedTime = in.readLong();
+    thisAccessedTime = in.readLong();
+    maxInactiveInterval = in.readInt();
+    isNew = in.readBoolean();
+    isValid = in.readBoolean();
     readInAttributes(in);
-    this.serializedPrincipal = DataSerializer.readByteArray(in);
+    serializedPrincipal = DataSerializer.readByteArray(in);
 
     // Read the DeltaSession state
-    this.enableGatewayDeltaReplication = in.readBoolean();
-    this.sessionRegionName = DataSerializer.readString(in);
+    enableGatewayDeltaReplication = in.readBoolean();
+    sessionRegionName = DataSerializer.readString(in);
 
     // This allows for backwards compatibility with 2.1 clients
     if (((InputStream) in).available() > 0) {
-      this.contextName = DataSerializer.readString(in);
+      contextName = DataSerializer.readString(in);
     }
 
     // Initialize the transients if necessary
-    if (this.listeners == null) {
-      this.listeners = new ArrayList();
+    if (listeners == null) {
+      listeners = new ArrayList<>();
     }
 
-    if (this.notes == null) {
-      this.notes = new Hashtable();
+    if (notes == null) {
+      notes = new Hashtable<>();
     }
   }
 
   private void readInAttributes(DataInput in) throws IOException, ClassNotFoundException {
-    ConcurrentHashMap map = DataSerializer.readObject(in);
+    ConcurrentHashMap<Object, Object> map = DataSerializer.readObject(in);
     try {
       Field field = getAttributesFieldObject();
       field.set(this, map);
@@ -616,9 +666,9 @@ public class DeltaSession extends StandardSession
   }
 
   private void logError(Exception e) {
-    if (getManager() != null) {
-      DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-      mgr.getLogger().error(e);
+    final DeltaSessionManager<?> deltaSessionManager = getDeltaSessionManager();
+    if (deltaSessionManager != null) {
+      deltaSessionManager.getLogger().error(e);
     }
   }
 
@@ -626,7 +676,7 @@ public class DeltaSession extends StandardSession
   public int getSizeInBytes() {
     int size = 0;
     @SuppressWarnings("unchecked")
-    Enumeration<String> attributeNames = (Enumeration<String>) getAttributeNames();
+    Enumeration<String> attributeNames = getAttributeNames();
     while (attributeNames.hasMoreElements()) {
       // Don't use getAttribute() because we don't want to deserialize the value.
       Object value = getAttributeWithoutDeserialize(attributeNames.nextElement());
@@ -669,7 +719,7 @@ public class DeltaSession extends StandardSession
     } catch (IOException e) {
       String builder = this + ": Object " + obj
           + " cannot be serialized due to the following exception";
-      ((DeltaSessionManager) getManager()).getLogger().warn(
+      getDeltaSessionManager().getLogger().warn(
           builder, e);
     }
     return serializedValue;
@@ -683,16 +733,16 @@ public class DeltaSession extends StandardSession
   @Override
   public String toString() {
     return "DeltaSession[" + "id=" + getId()
-        + "; context=" + this.contextName + "; sessionRegionName="
-        + this.sessionRegionName + "; operatingRegionName="
+        + "; context=" + contextName + "; sessionRegionName="
+        + sessionRegionName + "; operatingRegionName="
         + (getOperatingRegion() == null ? "unset" : getOperatingRegion().getFullPath())
         + "]";
   }
 
   // Helper methods to enable better unit testing
   DeltaSessionFacade getNewFacade(DeltaSessionInterface fSession) {
-    return (DeltaSessionFacade) AccessController.doPrivileged(
-        (PrivilegedAction) () -> new DeltaSessionFacade(fSession));
+    return AccessController.doPrivileged(
+        (PrivilegedAction<DeltaSessionFacade>) () -> new DeltaSessionFacade(fSession));
   }
 
   boolean isPackageProtectionEnabled() {
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManager.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManager.java
index 86103df..67455af 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManager.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManager.java
@@ -37,6 +37,8 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.servlet.http.HttpSession;
+
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
 import org.apache.catalina.Lifecycle;
@@ -63,9 +65,9 @@ import org.apache.geode.modules.util.ContextMapper;
 import org.apache.geode.modules.util.RegionConfiguration;
 import org.apache.geode.modules.util.RegionHelper;
 
-public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCommitSessionValve>
+public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCommitSessionValve<?>>
     extends ManagerBase
-    implements Lifecycle, PropertyChangeListener, SessionManager {
+    implements Lifecycle, PropertyChangeListener, SessionManager, DeltaSessionManagerConfiguration {
 
   static final String catalinaBaseSystemProperty = "catalina.base";
   static final String javaTempDirSystemProperty = "java.io.tmpdir";
@@ -73,7 +75,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   /**
    * The number of rejected sessions.
    */
-  private AtomicInteger rejectedSessions;
+  private final AtomicInteger rejectedSessions;
 
   /**
    * The maximum number of active Sessions allowed, or -1 for no limit.
@@ -106,6 +108,10 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   private static final boolean DEFAULT_ENABLE_COMMIT_VALVE_FAILFAST = false;
 
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
   private static final boolean DEFAULT_PREFER_DESERIALIZED_FORM = true;
 
   /*
@@ -117,7 +123,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   protected String regionName = DEFAULT_REGION_NAME;
 
   private String regionAttributesId; // the default is different for client-server and
-                                     // peer-to-peer
+  // peer-to-peer
 
   private Boolean enableLocalCache; // the default is different for client-server and peer-to-peer
 
@@ -129,6 +135,10 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   private boolean enableDebugListener = DEFAULT_ENABLE_DEBUG_LISTENER;
 
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
   private boolean preferDeserializedForm = DEFAULT_PREFER_DESERIALIZED_FORM;
 
   private Timer timer;
@@ -142,16 +152,17 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
       Long.getLong("gemfiremodules.sessionTimerTaskDelay", 10000);
 
   public DeltaSessionManager() {
-    this.rejectedSessions = new AtomicInteger(0);
+    rejectedSessions = new AtomicInteger(0);
     // Create the set to store sessions to be touched after get attribute requests
-    this.sessionsToTouch = Collections.newSetFromMap(new ConcurrentHashMap<>());
+    sessionsToTouch = Collections.newSetFromMap(new ConcurrentHashMap<>());
   }
 
   @Override
   public String getRegionName() {
-    return this.regionName;
+    return regionName;
   }
 
+  @Override
   public void setRegionName(String regionName) {
     this.regionName = regionName;
   }
@@ -166,15 +177,15 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     // This property will be null if it hasn't been set in the context.xml file.
     // Since its default is dependent on the session cache, get the default from
     // the session cache.
-    if (this.regionAttributesId == null) {
-      this.regionAttributesId = getSessionCache().getDefaultRegionAttributesId();
+    if (regionAttributesId == null) {
+      regionAttributesId = getSessionCache().getDefaultRegionAttributesId();
     }
-    return this.regionAttributesId;
+    return regionAttributesId;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setRegionAttributesId(String regionType) {
-    this.regionAttributesId = regionType;
+    regionAttributesId = regionType;
   }
 
   @Override
@@ -182,23 +193,23 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     // This property will be null if it hasn't been set in the context.xml file.
     // Since its default is dependent on the session cache, get the default from
     // the session cache.
-    if (this.enableLocalCache == null) {
-      this.enableLocalCache = getSessionCache().getDefaultEnableLocalCache();
+    if (enableLocalCache == null) {
+      enableLocalCache = getSessionCache().getDefaultEnableLocalCache();
     }
-    return this.enableLocalCache;
+    return enableLocalCache;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setEnableLocalCache(boolean enableLocalCache) {
     this.enableLocalCache = enableLocalCache;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public int getMaxActiveSessions() {
-    return this.maxActiveSessions;
+    return maxActiveSessions;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setMaxActiveSessions(int maxActiveSessions) {
     int oldMaxActiveSessions = this.maxActiveSessions;
     this.maxActiveSessions = maxActiveSessions;
@@ -212,7 +223,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     return false; // disabled
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setEnableGatewayDeltaReplication(boolean enableGatewayDeltaReplication) {
     // this.enableGatewayDeltaReplication = enableGatewayDeltaReplication;
     // Disabled. Keeping the method for backward compatibility.
@@ -220,41 +231,42 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   @Override
   public boolean getEnableGatewayReplication() {
-    return this.enableGatewayReplication;
+    return enableGatewayReplication;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setEnableGatewayReplication(boolean enableGatewayReplication) {
     this.enableGatewayReplication = enableGatewayReplication;
   }
 
   @Override
   public boolean getEnableDebugListener() {
-    return this.enableDebugListener;
+    return enableDebugListener;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setEnableDebugListener(boolean enableDebugListener) {
     this.enableDebugListener = enableDebugListener;
   }
 
   @Override
   public boolean isCommitValveEnabled() {
-    return this.enableCommitValve;
+    return enableCommitValve;
   }
 
+  @Override
   public void setEnableCommitValve(boolean enable) {
-    this.enableCommitValve = enable;
+    enableCommitValve = enable;
   }
 
   @Override
   public boolean isCommitValveFailfastEnabled() {
-    return this.enableCommitValveFailfast;
+    return enableCommitValveFailfast;
   }
 
-  @SuppressWarnings("unused")
+  @Override
   public void setEnableCommitValveFailfast(boolean enable) {
-    this.enableCommitValveFailfast = enable;
+    enableCommitValveFailfast = enable;
   }
 
   @Override
@@ -262,14 +274,27 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     return sessionCache.isBackingCacheAvailable();
   }
 
-  @SuppressWarnings("unused")
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
+  @Override
   public void setPreferDeserializedForm(boolean enable) {
-    this.preferDeserializedForm = enable;
+    log.warn("Use of deprecated preferDeserializedForm property to be removed in future release.");
+    if (!enable) {
+      log.warn(
+          "Use of HttpSessionAttributeListener may result in serialized form in HttpSessionBindingEvent.");
+    }
+    preferDeserializedForm = enable;
   }
 
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
   @Override
   public boolean getPreferDeserializedForm() {
-    return this.preferDeserializedForm;
+    return preferDeserializedForm;
   }
 
   @Override
@@ -286,7 +311,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   }
 
   public SessionCache getSessionCache() {
-    return this.sessionCache;
+    return sessionCache;
   }
 
   public DeltaSessionStatistics getStatistics() {
@@ -297,7 +322,6 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     return getSessionCache().isPeerToPeer();
   }
 
-  @SuppressWarnings("unused")
   public boolean isClientServer() {
     return getSessionCache().isClientServer();
   }
@@ -330,7 +354,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   }
 
   @Override
-  public Session findSession(String id) throws IOException {
+  public Session findSession(String id) {
     if (id == null) {
       return null;
     }
@@ -386,7 +410,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     }
 
     // Create the appropriate session cache
-    this.sessionCache = cache.isClient() ? new ClientServerSessionCache(this, cache)
+    sessionCache = cache.isClient() ? new ClientServerSessionCache(this, cache)
         : new PeerToPeerSessionCache(this, cache);
 
     // Initialize the session cache
@@ -394,7 +418,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   }
 
   void initSessionCache() {
-    this.sessionCache.initialize();
+    sessionCache.initialize();
   }
 
   Cache getAnyCacheInstance() {
@@ -408,11 +432,6 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   @Override
   public void remove(Session session) {
-    remove(session, false);
-  }
-
-  public void remove(Session session, @SuppressWarnings("unused") boolean update) {
-    // super.remove(session);
     // Remove the session from the region if necessary.
     // It will have already been removed if it expired implicitly.
     DeltaSessionInterface ds = (DeltaSessionInterface) session;
@@ -451,7 +470,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   @Override
   public int getRejectedSessions() {
-    return this.rejectedSessions.get();
+    return rejectedSessions.get();
   }
 
   @Override
@@ -498,20 +517,20 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
    * expiration.
    */
   void addSessionToTouch(String sessionId) {
-    this.sessionsToTouch.add(sessionId);
+    sessionsToTouch.add(sessionId);
   }
 
   protected Set<String> getSessionsToTouch() {
-    return this.sessionsToTouch;
+    return sessionsToTouch;
   }
 
-  boolean removeTouchedSession(String sessionId) {
-    return this.sessionsToTouch.remove(sessionId);
+  void removeTouchedSession(String sessionId) {
+    sessionsToTouch.remove(sessionId);
   }
 
   protected void scheduleTimerTasks() {
     // Create the timer
-    this.timer = new Timer("Timer for " + toString(), true);
+    timer = new Timer("Timer for " + toString(), true);
 
     // Schedule the task to handle sessions to be touched
     scheduleTouchSessionsTask();
@@ -538,12 +557,12 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
         }
       }
     };
-    this.timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
+    timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
   }
 
   protected void cancelTimer() {
     if (timer != null) {
-      this.timer.cancel();
+      timer.cancel();
     }
   }
 
@@ -562,7 +581,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
       }
     };
 
-    this.timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
+    timer.schedule(task, TIMER_TASK_DELAY, TIMER_TASK_PERIOD);
   }
 
   @Override
@@ -698,7 +717,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
       getLogger().debug("Query: " + query.getQueryString());
     }
 
-    SelectResults results;
+    SelectResults<String> results;
     try {
       results = (SelectResults) query.execute();
     } catch (Exception ex) {
@@ -721,7 +740,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     }
     FileOutputStream fos = null;
     BufferedOutputStream bos = null;
-    ObjectOutputStream oos = null;
+    final ObjectOutputStream oos;
     boolean error = false;
     try {
       fos = getFileOutputStream(store);
@@ -733,13 +752,6 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
       throw e;
     } finally {
       if (error) {
-        if (oos != null) {
-          try {
-            oos.close();
-          } catch (IOException ioe) {
-            // Ignore
-          }
-        }
         if (bos != null) {
           try {
             bos.close();
@@ -758,10 +770,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     }
 
     ArrayList<DeltaSessionInterface> list = new ArrayList<>();
-    @SuppressWarnings("unchecked")
-    Iterator<String> elements = (Iterator<String>) results.iterator();
-    while (elements.hasNext()) {
-      String id = elements.next();
+    for (final String id : results) {
       DeltaSessionInterface session = (DeltaSessionInterface) findSession(id);
       if (session != null) {
         list.add(session);
@@ -769,8 +778,9 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     }
 
     // Write the number of active sessions, followed by the details
-    if (getLogger().isDebugEnabled())
+    if (getLogger().isDebugEnabled()) {
       getLogger().debug("Unloading " + list.size() + " sessions");
+    }
     try {
       writeToObjectOutputStream(oos, list);
       for (DeltaSessionInterface session : list) {
@@ -828,7 +838,8 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
 
   /**
    * Load any currently active sessions that were previously unloaded to the appropriate persistence
-   * mechanism, if any. If persistence is not supported, this method returns without doing anything.
+   * mechanism, if any. If persistence is not supported, this method returns without doing
+   * anything.
    *
    * @throws ClassNotFoundException if a serialized class cannot be found during the reload
    * @throws IOException if an input/output error occurs
@@ -904,8 +915,9 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
         session.readObjectData(ois);
         session.setManager(this);
 
-        Region region = getSessionCache().getOperatingRegion();
-        DeltaSessionInterface existingSession = (DeltaSessionInterface) region.get(session.getId());
+        final Region<String, HttpSession> region = getSessionCache().getOperatingRegion();
+        final DeltaSessionInterface existingSession =
+            (DeltaSessionInterface) region.get(session.getId());
         // Check whether the existing session is newer
         if (existingSession != null
             && existingSession.getLastAccessedTime() > session.getLastAccessedTime()) {
@@ -998,7 +1010,7 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
     return new ObjectOutputStream(bos);
   }
 
-  void writeToObjectOutputStream(ObjectOutputStream oos, List listToWrite) throws IOException {
+  void writeToObjectOutputStream(ObjectOutputStream oos, List<?> listToWrite) throws IOException {
     oos.writeObject(listToWrite.size());
   }
 
@@ -1010,8 +1022,8 @@ public abstract class DeltaSessionManager<CommitSessionValveT extends AbstractCo
   @Override
   public String toString() {
     return getClass().getSimpleName() + "[" + "container="
-        + getTheContext() + "; regionName=" + this.regionName
-        + "; regionAttributesId=" + this.regionAttributesId + "]";
+        + getTheContext() + "; regionName=" + regionName
+        + "; regionAttributesId=" + regionAttributesId + "]";
   }
 
   String getContextName() {
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerConfiguration.java
similarity index 57%
copy from extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java
copy to extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerConfiguration.java
index 52337c1..361f3ae 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerConfiguration.java
@@ -12,35 +12,63 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package org.apache.geode.modules.session.catalina;
 
-import org.apache.juli.logging.Log;
+/**
+ * Method used by Catalina XML configuration.
+ */
+@SuppressWarnings("unused")
+interface DeltaSessionManagerConfiguration {
 
-public interface SessionManager {
+  void setRegionName(String regionName);
 
   String getRegionName();
 
+  void setEnableLocalCache(boolean enableLocalCache);
+
+  boolean getEnableLocalCache();
+
+  void setMaxActiveSessions(int maxActiveSessions);
+
+  int getMaxActiveSessions();
+
+  void setRegionAttributesId(String regionType);
+
   String getRegionAttributesId();
 
-  int getMaxInactiveInterval();
+  void setEnableGatewayDeltaReplication(boolean enableGatewayDeltaReplication);
+
+  boolean getEnableGatewayDeltaReplication();
+
+  void setEnableGatewayReplication(boolean enableGatewayReplication);
 
   boolean getEnableGatewayReplication();
 
-  boolean getEnableGatewayDeltaReplication();
+  void setEnableDebugListener(boolean enableDebugListener);
 
   boolean getEnableDebugListener();
 
-  boolean getEnableLocalCache();
-
   boolean isCommitValveEnabled();
 
+  void setEnableCommitValve(boolean enable);
+
   boolean isCommitValveFailfastEnabled();
 
+  void setEnableCommitValveFailfast(boolean enable);
+
   boolean isBackingCacheAvailable();
 
-  boolean getPreferDeserializedForm();
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
+  void setPreferDeserializedForm(boolean enable);
 
-  String getStatisticsName();
+  /**
+   * @deprecated No replacement. Always prefer deserialized form.
+   */
+  @Deprecated
+  boolean getPreferDeserializedForm();
 
-  Log getLogger();
 }
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/JvmRouteBinderValve.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/JvmRouteBinderValve.java
index 47b70f8..012973c 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/JvmRouteBinderValve.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/JvmRouteBinderValve.java
@@ -64,15 +64,7 @@ public class JvmRouteBinderValve extends ValveBase {
           manager.getLogger().debug(builder);
         }
         // Get the original session
-        Session session = null;
-        try {
-          session = manager.findSession(sessionId);
-        } catch (IOException e) {
-          String builder = this + ": Caught exception attempting to find session "
-              + sessionId + " in " + manager;
-          manager.getLogger().warn(builder, e);
-        }
-
+        final Session session = manager.findSession(sessionId);
         if (session == null) {
           String builder = this + ": Did not find session " + sessionId
               + " to failover in " + manager;
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java
index 52337c1..998019f 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/SessionManager.java
@@ -38,6 +38,10 @@ public interface SessionManager {
 
   boolean isBackingCacheAvailable();
 
+  /**
+   * @deprecated no replacement. Always prefer deserialized form.
+   */
+  @Deprecated
   boolean getPreferDeserializedForm();
 
   String getStatisticsName();
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
index b27fe65..adb0c88 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
@@ -18,7 +18,8 @@ package org.apache.geode.modules.session.catalina;
 import org.apache.catalina.connector.Response;
 
 @Deprecated
-public final class Tomcat6CommitSessionValve extends AbstractCommitSessionValve {
+public final class Tomcat6CommitSessionValve
+    extends AbstractCommitSessionValve<Tomcat6CommitSessionValve> {
 
   @Override
   protected Response wrapResponse(Response response) {
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/callback/SessionExpirationCacheListener.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/callback/SessionExpirationCacheListener.java
index 3b62b31..eb93113 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/callback/SessionExpirationCacheListener.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/callback/SessionExpirationCacheListener.java
@@ -30,7 +30,7 @@ public class SessionExpirationCacheListener extends CacheListenerAdapter<String,
 
   @Override
   public void afterDestroy(EntryEvent<String, HttpSession> event) {
-    // A Session expired. If it was destroyed by GemFire expiration, process it.
+    // A Session expired. If it was destroyed by Geode expiration, process it.
     // If it was destroyed via Session.invalidate, ignore it since it has
     // already been processed.
     DeltaSessionInterface session = null;
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/AccessAttributeValueListener.java
similarity index 53%
copy from extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
copy to extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/AccessAttributeValueListener.java
index b27fe65..1a083ab 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/Tomcat6CommitSessionValve.java
+++ b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/AccessAttributeValueListener.java
@@ -12,16 +12,24 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
+package org.apache.geode.modules.session;
 
-package org.apache.geode.modules.session.catalina;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
 
-import org.apache.catalina.connector.Response;
+public class AccessAttributeValueListener implements HttpSessionAttributeListener {
+  @Override
+  public void attributeAdded(HttpSessionBindingEvent event) {
+    System.out.println("event created value is " + (String) event.getValue());
+  }
 
-@Deprecated
-public final class Tomcat6CommitSessionValve extends AbstractCommitSessionValve {
+  @Override
+  public void attributeRemoved(HttpSessionBindingEvent event) {
+    System.out.println("event removed value is " + (String) event.getValue());
+  }
 
   @Override
-  protected Response wrapResponse(Response response) {
-    return response;
+  public void attributeReplaced(HttpSessionBindingEvent event) {
+    System.out.println("event replaced value is " + (String) event.getValue());
   }
 }
diff --git a/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml b/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
index 3746c08..42afa86 100644
--- a/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
+++ b/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
@@ -48,4 +48,8 @@ limitations under the License.
     <listener-class>org.apache.geode.modules.session.SessionCountingListener</listener-class>
   </listener>
 
+  <listener>
+    <listener-class>org.apache.geode.modules.session.AccessAttributeValueListener</listener-class>
+  </listener>
+
 </web-app>
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/CargoTestBase.java b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/CargoTestBase.java
index 6299a8f..9e0a3ef 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/CargoTestBase.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/CargoTestBase.java
@@ -16,6 +16,7 @@ package org.apache.geode.session.tests;
 
 import static org.junit.Assert.assertEquals;
 
+import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.nio.file.Files;
@@ -99,7 +100,7 @@ public abstract class CargoTestBase {
    * Stops all containers that were previously started and cleans up their configurations
    */
   @After
-  public void stop() throws IOException, InterruptedException {
+  public void stop() throws IOException {
     try {
       manager.stopAllActiveContainers();
     } finally {
@@ -119,7 +120,7 @@ public abstract class CargoTestBase {
    * Gets the specified key from all the containers within the container manager and check that each
    * container has the associated expected value
    */
-  public void getKeyValueDataOnAllClients(String key, String expectedValue, String expectedCookie)
+  private void getKeyValueDataOnAllClients(String key, String expectedValue, String expectedCookie)
       throws IOException, URISyntaxException {
     for (int i = 0; i < manager.numContainers(); i++) {
       // Set the port for this server
@@ -128,9 +129,10 @@ public abstract class CargoTestBase {
       Client.Response resp = client.get(key);
 
       // Null would mean we don't expect the same cookie as before
-      if (expectedCookie != null)
+      if (expectedCookie != null) {
         assertEquals("Sessions are not replicating properly", expectedCookie,
             resp.getSessionCookie());
+      }
 
       // Check that the response from this server is correct
       if (install.getConnectionType() == ContainerInstall.ConnectionType.CACHING_CLIENT_SERVER) {
@@ -208,6 +210,8 @@ public abstract class CargoTestBase {
     manager.removeContainer(0);
 
     getKeyValueDataOnAllClients(key, value, resp.getSessionCookie());
+
+    checkLogs();
   }
 
   /**
@@ -226,6 +230,8 @@ public abstract class CargoTestBase {
     client.invalidate();
 
     verifySessionIsRemoved(key);
+
+    checkLogs();
   }
 
   protected void verifySessionIsRemoved(String key) throws IOException, URISyntaxException {
@@ -253,6 +259,8 @@ public abstract class CargoTestBase {
     Thread.sleep(5000);
 
     verifySessionIsRemoved(key);
+
+    checkLogs();
   }
 
   /**
@@ -261,7 +269,7 @@ public abstract class CargoTestBase {
    */
   @Test
   public void sessionPicksUpSessionTimeoutConfiguredInWebXml()
-      throws IOException, URISyntaxException, InterruptedException {
+      throws IOException, URISyntaxException {
     manager.startAllInactiveContainers();
 
     String key = "value_testSessionExpiration";
@@ -276,6 +284,7 @@ public abstract class CargoTestBase {
     client.setMaxInactive(63);
     verifyMaxInactiveInterval(63);
 
+    checkLogs();
   }
 
   protected void verifyMaxInactiveInterval(int expected) throws IOException, URISyntaxException {
@@ -326,6 +335,7 @@ public abstract class CargoTestBase {
     }
 
     getKeyValueDataOnAllClients(key, value, resp.getSessionCookie());
+    checkLogs();
   }
 
   /**
@@ -347,6 +357,8 @@ public abstract class CargoTestBase {
     client.remove(key);
 
     getKeyValueDataOnAllClients(key, "", resp.getSessionCookie());
+
+    checkLogs();
   }
 
   /**
@@ -377,6 +389,31 @@ public abstract class CargoTestBase {
     getKeyValueDataOnAllClients(key, value, resp.getSessionCookie());
   }
 
+  @Test
+  public void attributesCanBeReplaced() throws IOException, URISyntaxException {
+    manager.startAllInactiveContainers();
+    String key = "value_testSessionUpdate";
+    String value = "Foo";
+    String updateValue = "Bar";
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response response = client.set(key, value);
+    GeodeAwaitility.await()
+        .untilAsserted(() -> getKeyValueDataOnAllClients(key, value, response.getSessionCookie()));
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response updateResponse = client.set(key, updateValue);
+    GeodeAwaitility.await().untilAsserted(
+        () -> getKeyValueDataOnAllClients(key, updateValue, updateResponse.getSessionCookie()));
+
+    checkLogs();
+  }
+
+  private void checkLogs() {
+    for (int i = 0; i < manager.numContainers(); i++) {
+      File cargo_dir = manager.getContainer(i).cargoLogDir;
+      LogChecker.checkLogs(cargo_dir);
+    }
+  }
+
   private void announceTest(String status) {
     System.out.format("TEST %s %s.%s%n", status, getClass().getSimpleName(),
         testName.getMethodName());
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/LogChecker.java b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/LogChecker.java
new file mode 100644
index 0000000..001487d
--- /dev/null
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/LogChecker.java
@@ -0,0 +1,126 @@
+/*
+ * 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.session.tests;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.logging.internal.log4j.api.LogService;
+
+public class LogChecker {
+  private static List<String> suspectStrings;
+  private static List<String> excludeStrings;
+  private static Logger logger = LogService.getLogger();
+
+  static {
+    suspectStrings = new ArrayList<>();
+    suspectStrings.add(java.lang.ClassCastException.class.getName());
+    suspectStrings.add(java.lang.NullPointerException.class.getName());
+    excludeStrings = new ArrayList<>();
+    excludeStrings.add("[fine");
+  }
+
+  static void checkLogs(File dir) {
+    List<File> logsToCheck = getLogs(dir);
+    checkLogs(logsToCheck);
+  }
+
+  private static List<File> getLogs(File currentDir) {
+    logger.info("Getting all logs visible from " + currentDir);
+    List<File> logList = new ArrayList<>();
+    getLogs(currentDir, logList);
+    return logList;
+  }
+
+  private static void getLogs(File currentDir, List<File> logs) {
+    File[] dirContents = currentDir.listFiles();
+    if (dirContents == null) {
+      return;
+    }
+    for (File aFile : dirContents) {
+      if (aFile.isDirectory()) {
+        getLogs(aFile, logs);
+      } else {
+        String fileName = aFile.getName();
+        if (fileName.startsWith("container") && fileName.endsWith(".log")) {
+          logs.add(aFile);
+        } else if (fileName.startsWith("gemfire") && fileName.endsWith(".log")) {
+          logs.add(aFile);
+        }
+      }
+    }
+  }
+
+  private static void checkLogs(List<File> logsToCheck) {
+    BufferedReader reader = null;
+    String line;
+    for (File aFile : logsToCheck) {
+      logger.info("Checking " + aFile.getAbsolutePath());
+      try {
+        try {
+          reader = new BufferedReader(new FileReader(aFile.getAbsoluteFile()));
+        } catch (FileNotFoundException e) {
+          throw new RuntimeException(e);
+        }
+        line = readNextLine(reader);
+        while (line != null) {
+          if (contains(suspectStrings, line) && !contains(excludeStrings, line)) {
+            throw new RuntimeException(aFile.getAbsolutePath() + " contains " + line + "\n");
+          }
+          line = readNextLine(reader);
+        }
+      } finally {
+        close(reader);
+      }
+    }
+  }
+
+  private static String readNextLine(BufferedReader reader) {
+    String line;
+    try {
+      line = reader.readLine();
+      return line;
+    } catch (IOException e) {
+      logger.info("Caught " + e);
+      return null;
+    }
+  }
+
+  private static boolean contains(List<String> targetStrs, String aStr) {
+    for (String target : targetStrs) {
+      if (aStr.contains(target)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static void close(BufferedReader reader) {
+    if (reader != null) {
+      try {
+        reader.close();
+      } catch (IOException e) {
+        logger.info("Caught " + e + " while closing " + reader);
+      }
+    }
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
index 07524d4..dd2e24d 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
@@ -4023,7 +4023,7 @@ public class GemFireCacheImpl implements InternalCache, InternalClientCache, Has
 
   @Override
   public <K, V> RegionAttributes<K, V> getRegionAttributes(String id) {
-    return GemFireCacheImpl.UncheckedUtils.<K, V>uncheckedCast(namedRegionAttributes).get(id);
+    return (RegionAttributes<K, V>) namedRegionAttributes.get(id);
   }
 
   @Override