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:09 UTC

[geode] 03/12: Revert "GEODE-8513: Remove (de)serialization of local sessions."

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 1ed0bf31a985ed93c0bf957d1ccd0abbb3de7e2a
Author: Sarah <sa...@pivotal.io>
AuthorDate: Fri Apr 9 11:35:35 2021 -0400

    Revert "GEODE-8513: Remove (de)serialization of local sessions."
    
    This reverts commit 5eb8521253986c06b52ff02d00086380626bb262.
---
 .../catalina/DeltaSessionManagerJUnitTest.java     | 223 ++++++++++++-
 .../session/catalina/DeltaSessionManager.java      | 356 ++++++++++++++++++++-
 2 files changed, 565 insertions(+), 14 deletions(-)

diff --git a/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerJUnitTest.java b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerJUnitTest.java
index 835575b..fec8571 100644
--- a/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerJUnitTest.java
+++ b/extensions/geode-modules-test/src/main/java/org/apache/geode/modules/session/catalina/DeltaSessionManagerJUnitTest.java
@@ -17,28 +17,50 @@ package org.apache.geode.modules.session.catalina;
 
 import static org.apache.geode.modules.util.RegionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
 import java.beans.PropertyChangeEvent;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.util.HashSet;
 import java.util.Set;
 
 import javax.servlet.http.HttpSession;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.Loader;
 import org.apache.catalina.Session;
+import org.apache.catalina.session.StandardSession;
 import org.apache.juli.logging.Log;
 import org.junit.Test;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.FunctionDomainException;
+import org.apache.geode.cache.query.NameResolutionException;
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.QueryInvocationTargetException;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.TypeMismatchException;
+import org.apache.geode.cache.query.internal.InternalQueryService;
+import org.apache.geode.cache.query.internal.LinkedResultSet;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
 
@@ -195,15 +217,142 @@ public abstract class DeltaSessionManagerJUnitTest {
   }
 
   @Test
+  public void loadActivatesAndAddsSingleSessionWithValidIdAndMoreRecentAccessTime()
+      throws IOException, ClassNotFoundException {
+    String contextPath = "contextPath";
+    String expectedStoreDir = "";
+    DeltaSession newSession = mock(DeltaSession.class);
+    DeltaSession existingSession = mock(DeltaSession.class);
+
+    prepareMocksForLoadTest(contextPath, newSession, existingSession, expectedStoreDir);
+
+    manager.load();
+
+    verify(newSession).activate();
+    verify(manager).add(newSession);
+  }
+
+  @Test
+  public void loadLogsWarningAndDoesNotAddSessionWhenSessionStoreNotFound()
+      throws IOException, ClassNotFoundException {
+    String contextPath = "contextPath";
+    String expectedStoreDir = "";
+    DeltaSession newSession = mock(DeltaSession.class);
+    DeltaSession existingSession = mock(DeltaSession.class);
+
+    prepareMocksForLoadTest(contextPath, newSession, existingSession, expectedStoreDir);
+
+    doReturn(null).when(manager).getFileAtPath(any(), any());
+
+    manager.load();
+
+    verify(logger).debug("No session store file found");
+    verify(manager, times(0)).add(any());
+  }
+
+  @Test
+  public void loadDoesNotAddSessionToManagerWithValidIdAndLessRecentAccessTime()
+      throws IOException, ClassNotFoundException {
+    String contextPath = "contextPath";
+    String expectedStoreDir = "";
+    DeltaSession newSession = mock(DeltaSession.class);
+    DeltaSession existingSession = mock(DeltaSession.class);
+
+    prepareMocksForLoadTest(contextPath, newSession, existingSession, expectedStoreDir);
+
+    when(existingSession.getLastAccessedTime()).thenReturn(2L);
+
+    manager.load();
+
+    verify(newSession, times(0)).activate();
+    verify(manager, times(0)).add(newSession);
+  }
+
+  @Test
+  public void unloadWritesSingleSessionToDiskWhenIdIsValid()
+      throws IOException, NameResolutionException, TypeMismatchException,
+      QueryInvocationTargetException, FunctionDomainException {
+    String sessionId = "sessionId";
+    DeltaSession session = mock(DeltaSession.class);
+    FileOutputStream fos = mock(FileOutputStream.class);
+    BufferedOutputStream bos = mock(BufferedOutputStream.class);
+    ObjectOutputStream oos = mock(ObjectOutputStream.class);
+
+    prepareMocksForUnloadTest(sessionId, fos, bos, oos, session);
+
+    manager.unload();
+
+    verify((StandardSession) session).writeObjectData(oos);
+  }
+
+  @Test
+  public void unloadDoesNotWriteSessionToDiskAndClosesOutputStreamsWhenOutputStreamThrowsIOException()
+      throws IOException, NameResolutionException, TypeMismatchException,
+      QueryInvocationTargetException, FunctionDomainException {
+    String sessionId = "sessionId";
+    DeltaSession session = mock(DeltaSession.class);
+    FileOutputStream fos = mock(FileOutputStream.class);
+    BufferedOutputStream bos = mock(BufferedOutputStream.class);
+    ObjectOutputStream oos = mock(ObjectOutputStream.class);
+
+    prepareMocksForUnloadTest(sessionId, fos, bos, oos, session);
+
+    String exceptionMessage = "Output Stream IOException";
+
+    IOException exception = new IOException(exceptionMessage);
+
+    doThrow(exception).when(manager).getObjectOutputStream(bos);
+
+    assertThatThrownBy(() -> manager.unload()).isInstanceOf(IOException.class)
+        .hasMessage(exceptionMessage);
+
+    verify((StandardSession) session, times(0)).writeObjectData(oos);
+    verify(bos).close();
+    verify(fos).close();
+  }
+
+  @Test
+  public void unloadDoesNotWriteSessionToDiskAndClosesOutputStreamsWhenSessionIsWrongClass()
+      throws IOException, NameResolutionException, TypeMismatchException,
+      QueryInvocationTargetException, FunctionDomainException {
+    String sessionId = "sessionId";
+    DeltaSession session = mock(DeltaSession.class);
+    FileOutputStream fos = mock(FileOutputStream.class);
+    BufferedOutputStream bos = mock(BufferedOutputStream.class);
+    ObjectOutputStream oos = mock(ObjectOutputStream.class);
+
+    prepareMocksForUnloadTest(sessionId, fos, bos, oos, session);
+
+    Session invalidSession =
+        mock(Session.class, withSettings().extraInterfaces(DeltaSessionInterface.class));
+
+    doReturn(invalidSession).when(manager).findSession(sessionId);
+
+    assertThatThrownBy(() -> manager.unload()).isInstanceOf(IOException.class);
+
+    verify((StandardSession) session, times(0)).writeObjectData(oos);
+    verify(oos).close();
+  }
+
+  @Test
   public void successfulUnloadWithClientServerSessionCachePerformsLocalDestroy()
-      throws IOException {
-    when(sessionCache.getCache()).thenReturn(cache);
-    when(context.getPath()).thenReturn("contextPath");
+      throws IOException, NameResolutionException, TypeMismatchException,
+      QueryInvocationTargetException, FunctionDomainException {
+    String sessionId = "sessionId";
+    DeltaSession session = mock(DeltaSession.class);
+    FileOutputStream fos = mock(FileOutputStream.class);
+    BufferedOutputStream bos = mock(BufferedOutputStream.class);
+    ObjectOutputStream oos = mock(ObjectOutputStream.class);
+
+    prepareMocksForUnloadTest(sessionId, fos, bos, oos, session);
+
     when(sessionCache.isClientServer()).thenReturn(true);
+    when(session.getId()).thenReturn(sessionId);
 
     manager.unload();
 
-    verify(operatingRegion).localClear();
+    verify((StandardSession) session).writeObjectData(oos);
+    verify(operatingRegion).localDestroy(sessionId);
   }
 
   @Test
@@ -253,4 +402,70 @@ public abstract class DeltaSessionManagerJUnitTest {
 
     verify(manager).setMaxInactiveInterval(oldValue);
   }
+
+  public void prepareMocksForUnloadTest(String sessionId, FileOutputStream fos,
+      BufferedOutputStream bos, ObjectOutputStream oos, DeltaSession session)
+      throws NameResolutionException, TypeMismatchException, QueryInvocationTargetException,
+      FunctionDomainException, IOException {
+    String regionName = "regionName";
+    String contextPath = "contextPath";
+    String catalinaBaseSystemProp = "Catalina/Base";
+    String systemFileSeparator = "/";
+    String expectedStoreDir = catalinaBaseSystemProp + systemFileSeparator + "temp";
+
+    InternalQueryService queryService = mock(InternalQueryService.class);
+    Query query = mock(Query.class);
+    File store = mock(File.class);
+    SelectResults results = new LinkedResultSet();
+
+    when(sessionCache.getCache()).thenReturn(cache);
+    when(context.getPath()).thenReturn(contextPath);
+    when(cache.getQueryService()).thenReturn(queryService);
+    when(queryService.newQuery(anyString())).thenReturn(query);
+    when(query.execute()).thenReturn(results);
+    doReturn(catalinaBaseSystemProp).when(manager)
+        .getSystemPropertyValue(DeltaSessionManager.catalinaBaseSystemProperty);
+    doReturn(systemFileSeparator).when(manager)
+        .getSystemPropertyValue(DeltaSessionManager.fileSeparatorSystemProperty);
+    doReturn(store).when(manager).getFileAtPath(expectedStoreDir, contextPath);
+    doReturn(fos).when(manager).getFileOutputStream(store);
+    doReturn(bos).when(manager).getBufferedOutputStream(fos);
+    doReturn(oos).when(manager).getObjectOutputStream(bos);
+    doReturn(regionName).when(manager).getRegionName();
+    doReturn(session).when(manager).findSession(sessionId);
+    doNothing().when(manager).writeToObjectOutputStream(any(), any());
+
+    results.add(sessionId);
+  }
+
+  public void prepareMocksForLoadTest(String contextPath, DeltaSession newSession,
+      DeltaSession existingSession, String expectedStoreDir)
+      throws IOException, ClassNotFoundException {
+    String catalinaBaseSystemProp = "Catalina/Base";
+    String systemFileSeparator = "/";
+    expectedStoreDir = catalinaBaseSystemProp + systemFileSeparator + "temp";
+    String newSessionId = "newSessionId";
+
+    File store = mock(File.class);
+    FileInputStream fis = mock(FileInputStream.class);
+    BufferedInputStream bis = mock(BufferedInputStream.class);
+    ObjectInputStream ois = mock(ObjectInputStream.class);
+    Loader loader = mock(Loader.class);
+
+    when(context.getPath()).thenReturn(contextPath);
+    when(context.getLoader()).thenReturn(loader);
+    when(newSession.getId()).thenReturn(newSessionId);
+    when(newSession.getLastAccessedTime()).thenReturn(1L);
+    when(newSession.isValid()).thenReturn(true);
+    when(existingSession.getLastAccessedTime()).thenReturn(0L);
+    doReturn(catalinaBaseSystemProp).when(manager).getSystemPropertyValue("catalina.base");
+    doReturn(systemFileSeparator).when(manager).getSystemPropertyValue("file.separator");
+    doReturn(store).when(manager).getFileAtPath(expectedStoreDir, contextPath);
+    doReturn(fis).when(manager).getFileInputStream(store);
+    doReturn(bis).when(manager).getBufferedInputStream(fis);
+    doReturn(ois).when(manager).getObjectInputStream(bis);
+    doReturn(1).when(manager).getSessionCountFromObjectInputStream(ois);
+    doReturn(newSession).when(manager).getNewSession();
+    doReturn(existingSession).when(operatingRegion).get(newSessionId);
+  }
 }
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 ec49b5d..2af36fe 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
@@ -16,10 +16,20 @@ package org.apache.geode.modules.session.catalina;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -30,16 +40,23 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
 import org.apache.catalina.Lifecycle;
+import org.apache.catalina.Loader;
 import org.apache.catalina.Pipeline;
 import org.apache.catalina.Session;
 import org.apache.catalina.Valve;
 import org.apache.catalina.session.ManagerBase;
 import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.CustomObjectInputStream;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.EntryNotFoundException;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.SelectResults;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.modules.session.catalina.internal.DeltaSessionStatistics;
 import org.apache.geode.modules.util.ContextMapper;
@@ -549,12 +566,13 @@ public abstract class DeltaSessionManager extends ManagerBase
 
   @Override
   public void load() throws ClassNotFoundException, IOException {
+    doLoad();
     ContextMapper.addContext(getContextName(), this);
   }
 
   @Override
   public void unload() throws IOException {
-    clearLocalCache();
+    doUnload();
     ContextMapper.removeContext(getContextName());
   }
 
@@ -650,22 +668,340 @@ public abstract class DeltaSessionManager extends ManagerBase
   }
 
   /**
-   * Clear the local cache to avoid ClassCastException if container is being reloaded.
+   * Save any currently active sessions in the appropriate persistence mechanism, if any. If
+   * persistence is not supported, this method returns without doing anything.
+   *
+   * @throws IOException if an input/output error occurs
    */
-  private void clearLocalCache() {
-    final Log logger = getLogger();
-    final boolean debugEnabled = logger.isDebugEnabled();
+  private void doUnload() throws IOException {
+    QueryService querySvc = getSessionCache().getCache().getQueryService();
+    Context context = getTheContext();
+
+    if (context == null) {
+      return;
+    }
+
+    String regionName;
+    if (getRegionName().startsWith("/")) {
+      regionName = getRegionName();
+    } else {
+      regionName = "/" + getRegionName();
+    }
+
+    Query query = querySvc.newQuery("select s.id from " + regionName
+        + " as s where s.contextName = '" + context.getPath() + "'");
+
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug("Query: " + query.getQueryString());
+    }
+
+    SelectResults results;
+    try {
+      results = (SelectResults) query.execute();
+    } catch (Exception ex) {
+      getLogger().error("Unable to perform query during doUnload", ex);
+      return;
+    }
+
+    if (results.isEmpty()) {
+      getLogger().debug("No sessions to unload for context " + context.getPath());
+      return; // nothing to do
+    }
 
+    // Open an output stream to the specified pathname, if any
+    File store = sessionStore(context.getPath());
+    if (store == null) {
+      return;
+    }
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug("Unloading sessions to " + store.getAbsolutePath());
+    }
+    FileOutputStream fos = null;
+    BufferedOutputStream bos = null;
+    ObjectOutputStream oos = null;
+    boolean error = false;
+    try {
+      fos = getFileOutputStream(store);
+      bos = getBufferedOutputStream(fos);
+      oos = getObjectOutputStream(bos);
+    } catch (IOException e) {
+      error = true;
+      getLogger().error("Exception unloading sessions", e);
+      throw e;
+    } finally {
+      if (error) {
+        if (oos != null) {
+          try {
+            oos.close();
+          } catch (IOException ioe) {
+            // Ignore
+          }
+        }
+        if (bos != null) {
+          try {
+            bos.close();
+          } catch (IOException ioe) {
+            // Ignore
+          }
+        }
+        if (fos != null) {
+          try {
+            fos.close();
+          } catch (IOException ioe) {
+            // Ignore
+          }
+        }
+      }
+    }
+
+    ArrayList<DeltaSessionInterface> list = new ArrayList<>();
+    @SuppressWarnings("unchecked")
+    Iterator<String> elements = (Iterator<String>) results.iterator();
+    while (elements.hasNext()) {
+      String id = elements.next();
+      DeltaSessionInterface session = (DeltaSessionInterface) findSession(id);
+      if (session != null) {
+        list.add(session);
+      }
+    }
+
+    // Write the number of active sessions, followed by the details
+    if (getLogger().isDebugEnabled())
+      getLogger().debug("Unloading " + list.size() + " sessions");
+    try {
+      writeToObjectOutputStream(oos, list);
+      for (DeltaSessionInterface session : list) {
+        if (session instanceof StandardSession) {
+          StandardSession standardSession = (StandardSession) session;
+          standardSession.passivate();
+          standardSession.writeObjectData(oos);
+        } else {
+          // All DeltaSessionInterfaces as of Geode 1.0 should be based on StandardSession
+          throw new IOException("Session should be of type StandardSession");
+        }
+      }
+    } catch (IOException e) {
+      getLogger().error("Exception unloading sessions", e);
+      try {
+        oos.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+      throw e;
+    }
+
+    // Flush and close the output stream
+    try {
+      oos.flush();
+    } finally {
+      try {
+        oos.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+    }
+
+    // Locally destroy the sessions we just wrote
     if (getSessionCache().isClientServer()) {
-      if (debugEnabled) {
-        logger.debug("Locally clearing sessions.");
+      for (DeltaSessionInterface session : list) {
+        if (getLogger().isDebugEnabled()) {
+          getLogger().debug("Locally destroying session " + session.getId());
+        }
+        try {
+          getSessionCache().getOperatingRegion().localDestroy(session.getId());
+        } catch (EntryNotFoundException ex) {
+          // This can be thrown if an entry is evicted during or immediately after a session is
+          // written
+          // to disk. This isn't a problem, but the resulting exception messages can be confusing in
+          // testing
+        }
+      }
+    }
+
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug("Unloading complete");
+    }
+  }
+
+  /**
+   * 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.
+   *
+   * @throws ClassNotFoundException if a serialized class cannot be found during the reload
+   * @throws IOException if an input/output error occurs
+   */
+  private void doLoad() throws ClassNotFoundException, IOException {
+    Context context = getTheContext();
+    if (context == null) {
+      return;
+    }
+
+    // Open an input stream to the specified pathname, if any
+    File store = sessionStore(context.getPath());
+    if (store == null) {
+      getLogger().debug("No session store file found");
+      return;
+    }
+    if (getLogger().isDebugEnabled()) {
+      getLogger().debug("Loading sessions from " + store.getAbsolutePath());
+    }
+    FileInputStream fis = null;
+    BufferedInputStream bis = null;
+    ObjectInputStream ois;
+    Loader loader = null;
+    ClassLoader classLoader = null;
+    try {
+      fis = getFileInputStream(store);
+      bis = getBufferedInputStream(fis);
+      if (getTheContext() != null) {
+        loader = getTheContext().getLoader();
+      }
+      if (loader != null) {
+        classLoader = loader.getClassLoader();
+      }
+      if (classLoader != null) {
+        if (getLogger().isDebugEnabled()) {
+          getLogger().debug("Creating custom object input stream for class loader");
+        }
+        ois = new CustomObjectInputStream(bis, classLoader);
+      } else {
+        if (getLogger().isDebugEnabled()) {
+          getLogger().debug("Creating standard object input stream");
+        }
+        ois = getObjectInputStream(bis);
+      }
+    } catch (FileNotFoundException e) {
+      if (getLogger().isDebugEnabled()) {
+        getLogger().debug("No persisted data file found");
       }
-      getSessionCache().getOperatingRegion().localClear();
+      return;
+    } catch (IOException e) {
+      getLogger().error("Exception loading sessions", e);
+      try {
+        fis.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+      try {
+        bis.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+      throw e;
     }
 
-    if (debugEnabled) {
-      logger.debug("Unloading complete");
+    // Load the previously unloaded active sessions
+    try {
+      int n = getSessionCountFromObjectInputStream(ois);
+      if (getLogger().isDebugEnabled()) {
+        getLogger().debug("Loading " + n + " persisted sessions");
+      }
+      for (int i = 0; i < n; i++) {
+        StandardSession session = getNewSession();
+        session.readObjectData(ois);
+        session.setManager(this);
+
+        Region region = getSessionCache().getOperatingRegion();
+        DeltaSessionInterface existingSession = (DeltaSessionInterface) region.get(session.getId());
+        // Check whether the existing session is newer
+        if (existingSession != null
+            && existingSession.getLastAccessedTime() > session.getLastAccessedTime()) {
+          if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Loaded session " + session.getId() + " is older than cached copy");
+          }
+          continue;
+        }
+
+        // Check whether the new session has already expired
+        if (!session.isValid()) {
+          if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Loaded session " + session.getId() + " is invalid");
+          }
+          continue;
+        }
+
+        getLogger().debug("Loading session " + session.getId());
+        session.activate();
+        add(session);
+      }
+    } catch (ClassNotFoundException | IOException e) {
+      getLogger().error(e);
+      try {
+        ois.close();
+      } catch (IOException f) {
+        // Ignore
+      }
+      throw e;
+    } finally {
+      // Close the input stream
+      try {
+        ois.close();
+      } catch (IOException f) {
+        // ignored
+      }
+
+      // Delete the persistent storage file
+      if (store.exists()) {
+        if (!store.delete()) {
+          getLogger().warn("Couldn't delete persistent storage file " + store.getAbsolutePath());
+        }
+      }
+    }
+  }
+
+  /**
+   * Return a File object representing the pathname to our persistence file, if any.
+   */
+  private File sessionStore(String ctxPath) {
+    String storeDir = getSystemPropertyValue(catalinaBaseSystemProperty);
+    if (storeDir == null || storeDir.isEmpty()) {
+      storeDir = getSystemPropertyValue(javaTempDirSystemProperty);
+    } else {
+      storeDir += getSystemPropertyValue(fileSeparatorSystemProperty) + "temp";
     }
+
+    return getFileAtPath(storeDir, ctxPath);
+  }
+
+  String getSystemPropertyValue(String propertyKey) {
+    return System.getProperty(propertyKey);
+  }
+
+  File getFileAtPath(String storeDir, String ctxPath) {
+    return (new File(storeDir, ctxPath.replaceAll("/", "_") + ".sessions.ser"));
+  }
+
+  FileInputStream getFileInputStream(File file) throws FileNotFoundException {
+    return new FileInputStream(file.getAbsolutePath());
+  }
+
+  BufferedInputStream getBufferedInputStream(FileInputStream fis) {
+    return new BufferedInputStream(fis);
+  }
+
+  ObjectInputStream getObjectInputStream(BufferedInputStream bis) throws IOException {
+    return new ObjectInputStream(bis);
+  }
+
+  FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
+    return new FileOutputStream(file.getAbsolutePath());
+  }
+
+  BufferedOutputStream getBufferedOutputStream(FileOutputStream fos) {
+    return new BufferedOutputStream(fos);
+  }
+
+  ObjectOutputStream getObjectOutputStream(BufferedOutputStream bos) throws IOException {
+    return new ObjectOutputStream(bos);
+  }
+
+  void writeToObjectOutputStream(ObjectOutputStream oos, List listToWrite) throws IOException {
+    oos.writeObject(listToWrite.size());
+  }
+
+  int getSessionCountFromObjectInputStream(ObjectInputStream ois)
+      throws IOException, ClassNotFoundException {
+    return (Integer) ois.readObject();
   }
 
   @Override