You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2017/08/19 00:10:33 UTC

[38/51] [abbrv] geode git commit: GEODE-3434: Allow the modules to be interoperable with current and older versions of tomcat 7

GEODE-3434: Allow the modules to be interoperable with current and older versions of tomcat 7

  * Modified DeltaSessions to use reflection to handle attributes fields incase an earlier tomcat 7 is used
  * Modified DeltaSession7 and DeltaSession8 to extend from DeltaSession
  * Added session backward compatibility tests
  * Modified aseembly build to download old product installs
  * Minor refactor of VersionManager to reuse property file load code


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/f38dff9d
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/f38dff9d
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/f38dff9d

Branch: refs/heads/feature/GEODE-1279
Commit: f38dff9d217a8808117b6fbb2e5f4021ef9c84ce
Parents: 0486700
Author: Jason Huynh <hu...@gmail.com>
Authored: Mon Aug 14 09:02:11 2017 -0700
Committer: Jason Huynh <hu...@gmail.com>
Committed: Thu Aug 17 17:00:14 2017 -0700

----------------------------------------------------------------------
 .../modules/session/catalina/DeltaSession7.java | 572 +------------------
 .../modules/session/catalina/DeltaSession8.java | 570 +-----------------
 .../modules/session/catalina/DeltaSession.java  |  50 +-
 geode-assembly/build.gradle                     |   3 +
 .../geode/session/tests/ContainerInstall.java   |  96 ++--
 .../geode/session/tests/TomcatInstall.java      |  68 +--
 ...TomcatSessionBackwardsCompatibilityTest.java | 244 ++++++++
 .../test/dunit/standalone/VersionManager.java   |  72 ++-
 .../standalone/VersionManagerJUnitTest.java     |   6 +-
 geode-old-versions/build.gradle                 |  64 ++-
 10 files changed, 495 insertions(+), 1250 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/extensions/geode-modules-tomcat7/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession7.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules-tomcat7/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession7.java b/extensions/geode-modules-tomcat7/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession7.java
index d7f30bd..f5dfbdc 100644
--- a/extensions/geode-modules-tomcat7/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession7.java
+++ b/extensions/geode-modules-tomcat7/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession7.java
@@ -14,88 +14,17 @@
  */
 package org.apache.geode.modules.session.catalina;
 
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.servlet.http.HttpSession;
-
 import org.apache.catalina.Manager;
-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.juli.logging.LogFactory;
-
-import org.apache.geode.DataSerializable;
-import org.apache.geode.DataSerializer;
-import org.apache.geode.Delta;
-import org.apache.geode.InvalidDeltaException;
-import org.apache.geode.cache.Region;
-import org.apache.geode.internal.cache.lru.Sizeable;
-import org.apache.geode.internal.util.BlobHelper;
-import org.apache.geode.modules.gatewaydelta.GatewayDelta;
-import org.apache.geode.modules.gatewaydelta.GatewayDeltaEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionAttributeEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionAttributeEventBatch;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionDestroyAttributeEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionUpdateAttributeEvent;
 
 @SuppressWarnings("serial")
-public class DeltaSession7 extends StandardSession
-    implements DataSerializable, Delta, GatewayDelta, Sizeable, DeltaSessionInterface {
-
-  private transient Region<String, HttpSession> operatingRegion;
-
-  private String sessionRegionName;
-
-  private String contextName;
-
-  private boolean hasDelta;
-
-  private boolean applyRemotely;
-
-  private boolean enableGatewayDeltaReplication;
-
-  private transient final Object changeLock = new Object();
-
-  private final List<DeltaSessionAttributeEvent> eventQueue =
-      new ArrayList<DeltaSessionAttributeEvent>();
-
-  private transient GatewayDeltaEvent currentGatewayDeltaEvent;
-
-  private transient boolean expired = false;
-
-  private transient boolean preferDeserializedForm = true;
-
-  private byte[] serializedPrincipal;
-
-  private final Log LOG = LogFactory.getLog(DeltaSession7.class.getName());
-
-  /**
-   * The string manager for this package.
-   */
-  // protected static StringManager STRING_MANAGER =
-  // StringManager.getManager("org.apache.geode.modules.session.catalina");
+public class DeltaSession7 extends DeltaSession {
 
   /**
    * Construct a new <code>Session</code> associated with no <code>Manager</code>. The
    * <code>Manager</code> will be assigned later using {@link #setOwner(Object)}.
    */
   public DeltaSession7() {
-    super(null);
+    super();
   }
 
   /**
@@ -105,503 +34,6 @@ public class DeltaSession7 extends StandardSession
    */
   public DeltaSession7(Manager manager) {
     super(manager);
-    setOwner(manager);
-  }
-
-  /**
-   * Return the <code>HttpSession</code> for which this object is the facade.
-   */
-  @SuppressWarnings("unchecked")
-  public HttpSession getSession() {
-    if (facade == null) {
-      if (SecurityUtil.isPackageProtectionEnabled()) {
-        final DeltaSession7 fsession = this;
-        facade = (DeltaSessionFacade) AccessController.doPrivileged(new PrivilegedAction() {
-          public Object run() {
-            return new DeltaSessionFacade(fsession);
-          }
-        });
-      } else {
-        facade = new DeltaSessionFacade(this);
-      }
-    }
-    return (facade);
-  }
-
-  public Principal getPrincipal() {
-    if (this.principal == null && this.serializedPrincipal != null) {
-      Principal sp = null;
-      try {
-        sp = (Principal) BlobHelper.deserializeBlob(this.serializedPrincipal);
-      } catch (Exception e) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(this).append(
-            ": Serialized principal contains a byte[] that cannot be deserialized due to the following exception");
-        ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-        return null;
-      }
-      this.principal = sp;
-      if (getManager() != null) {
-        DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-        if (mgr.getLogger().isDebugEnabled()) {
-          mgr.getLogger().debug(this + ": Deserialized principal: " + this.principal);
-        }
-      }
-    }
-    return this.principal;
   }
 
-  public void setPrincipal(Principal principal) {
-    super.setPrincipal(principal);
-
-    // Put the session into the region to serialize the principal
-    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);
-      }
-    }
-  }
-
-  private byte[] getSerializedPrincipal() {
-    if (this.serializedPrincipal == null) {
-      if (this.principal != null && this.principal instanceof GenericPrincipal) {
-        GenericPrincipal gp = (GenericPrincipal) this.principal;
-        this.serializedPrincipal = serialize(gp);
-        if (manager != null) {
-          DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-          if (mgr.getLogger().isDebugEnabled()) {
-            mgr.getLogger().debug(this + ": Serialized principal: " + gp);
-          }
-        }
-      }
-    }
-    return this.serializedPrincipal;
-  }
-
-  protected 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;
-  }
-
-  public boolean isCommitEnabled() {
-    DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-    return mgr.isCommitValveEnabled();
-  }
-
-  public GatewayDeltaEvent getCurrentGatewayDeltaEvent() {
-    return this.currentGatewayDeltaEvent;
-  }
-
-  public void setCurrentGatewayDeltaEvent(GatewayDeltaEvent currentGatewayDeltaEvent) {
-    this.currentGatewayDeltaEvent = currentGatewayDeltaEvent;
-  }
-
-  @SuppressWarnings("unchecked")
-  public void setOwner(Object manager) {
-    if (manager instanceof DeltaSessionManager) {
-      DeltaSessionManager sessionManager = (DeltaSessionManager) manager;
-      this.manager = sessionManager;
-      initializeRegion(sessionManager);
-      this.hasDelta = false;
-      this.applyRemotely = false;
-      this.enableGatewayDeltaReplication = sessionManager.getEnableGatewayDeltaReplication();
-      this.preferDeserializedForm = sessionManager.getPreferDeserializedForm();
-
-      // Initialize transient variables
-      if (this.listeners == null) {
-        this.listeners = new ArrayList();
-      }
-
-      if (this.notes == null) {
-        this.notes = new Hashtable();
-      }
-
-      contextName = ((DeltaSessionManager) manager).getContextName();
-    } else {
-      throw new IllegalArgumentException(this + ": The Manager must be an AbstractManager");
-    }
-  }
-
-  private void checkBackingCacheAvailable() {
-    if (!((SessionManager) getManager()).isBackingCacheAvailable()) {
-      throw new IllegalStateException("No backing cache server is available.");
-    }
-  }
-
-  public void setAttribute(String name, Object value, boolean notify) {
-    checkBackingCacheAvailable();
-    synchronized (this.changeLock) {
-      // Serialize the value
-      byte[] serializedValue = serialize(value);
-
-      // Store the attribute locally
-      if (this.preferDeserializedForm) {
-        super.setAttribute(name, value, true);
-      } else {
-        super.setAttribute(name, serializedValue, true);
-      }
-
-      if (serializedValue == null) {
-        return;
-      }
-
-      // Create the update attribute message
-      DeltaSessionAttributeEvent event =
-          new DeltaSessionUpdateAttributeEvent(name, serializedValue);
-      queueAttributeEvent(event, true);
-
-      // Distribute the update
-      if (!isCommitEnabled()) {
-        putInRegion(getOperatingRegion(), true, null);
-      }
-    }
-  }
-
-  public void removeAttribute(String name, boolean notify) {
-    checkBackingCacheAvailable();
-    if (expired) {
-      return;
-    }
-    synchronized (this.changeLock) {
-      // Remove the attribute locally
-      super.removeAttribute(name, true);
-
-      // Create the destroy attribute message
-      DeltaSessionAttributeEvent event = new DeltaSessionDestroyAttributeEvent(name);
-      queueAttributeEvent(event, true);
-
-      // Distribute the update
-      if (!isCommitEnabled()) {
-        putInRegion(getOperatingRegion(), true, null);
-      }
-    }
-  }
-
-  public Object getAttribute(String name) {
-    checkBackingCacheAvailable();
-    Object value = super.getAttribute(name);
-
-    // 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) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(this).append(": Attribute named ").append(name).append(
-            " contains a byte[] that cannot be deserialized due to the following exception");
-        ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-      }
-      if (this.preferDeserializedForm) {
-        localUpdateAttribute(name, value);
-      }
-    }
-
-    // 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;
-  }
-
-  public void invalidate() {
-    super.invalidate();
-    // getOperatingRegion().destroy(this.id, true); // already done in super (remove)
-    ((DeltaSessionManager) getManager()).getStatistics().incSessionsInvalidated();
-  }
-
-  public void processExpired() {
-    DeltaSessionManager manager = (DeltaSessionManager) getManager();
-    if (manager != null && manager.getLogger() != null && manager.getLogger().isDebugEnabled()) {
-      ((DeltaSessionManager) getManager()).getLogger().debug(this + ": Expired");
-    }
-
-    // Set expired (so region.destroy is not called again)
-    setExpired(true);
-
-    // Do expire processing
-    super.expire(true);
-
-    // Update statistics
-    if (manager != null) {
-      manager.getStatistics().incSessionsExpired();
-    }
-  }
-
-  @Override
-  public void expire(boolean notify) {
-    if (notify) {
-      getOperatingRegion().destroy(this.getId(), this);
-    } else {
-      super.expire(false);
-    }
-  }
-
-  public void setMaxInactiveInterval(int interval) {
-    super.setMaxInactiveInterval(interval);
-  }
-
-  public void localUpdateAttribute(String name, Object value) {
-    super.setAttribute(name, value, false); // don't do notification since this is a replication
-  }
-
-  public void localDestroyAttribute(String name) {
-    super.removeAttribute(name, false); // don't do notification since this is a replication
-  }
-
-  public void applyAttributeEvents(Region<String, DeltaSessionInterface> region,
-      List<DeltaSessionAttributeEvent> events) {
-    for (DeltaSessionAttributeEvent event : events) {
-      event.apply(this);
-      queueAttributeEvent(event, false);
-    }
-
-    putInRegion(region, false, true);
-  }
-
-  private void initializeRegion(DeltaSessionManager sessionManager) {
-    // Get the session region name
-    this.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();
-    if (sessionManager.getLogger().isDebugEnabled()) {
-      sessionManager.getLogger().debug(this + ": Set operating region: " + this.operatingRegion);
-    }
-  }
-
-  private void queueAttributeEvent(DeltaSessionAttributeEvent event,
-      boolean checkAddToCurrentGatewayDelta) {
-    // Add to current gateway delta if necessary
-    if (checkAddToCurrentGatewayDelta) {
-      // 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 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<DeltaSessionAttributeEvent>();
-          events.add(event);
-          this.currentGatewayDeltaEvent =
-              new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events);
-        }
-      }
-    }
-    this.eventQueue.add(event);
-  }
-
-  @SuppressWarnings("unchecked")
-  private void putInRegion(Region region, boolean applyRemotely, Object callbackArgument) {
-    this.hasDelta = true;
-    this.applyRemotely = applyRemotely;
-    region.put(this.id, this, callbackArgument);
-    this.eventQueue.clear();
-  }
-
-  public void commit() {
-    if (!isValidInternal())
-      throw new IllegalStateException("commit: Session " + getId() + " already invalidated");
-
-    synchronized (this.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()) {
-        setCurrentGatewayDeltaEvent(
-            new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, this.eventQueue));
-      }
-      this.hasDelta = true;
-      this.applyRemotely = true;
-      putInRegion(getOperatingRegion(), true, null);
-      this.eventQueue.clear();
-    }
-  }
-
-  public void abort() {
-    synchronized (this.changeLock) {
-      this.eventQueue.clear();
-    }
-  }
-
-  private void setExpired(boolean expired) {
-    this.expired = expired;
-  }
-
-  public boolean getExpired() {
-    return this.expired;
-  }
-
-  public String getContextName() {
-    return contextName;
-  }
-
-  public boolean hasDelta() {
-    return this.hasDelta;
-  }
-
-  public void toDelta(DataOutput out) throws IOException {
-    // Write whether to apply the changes to another DS if necessary
-    out.writeBoolean(this.applyRemotely);
-
-    // Write the events
-    DataSerializer.writeArrayList((ArrayList) this.eventQueue, out);
-
-    out.writeLong(this.lastAccessedTime);
-    out.writeInt(this.maxInactiveInterval);
-  }
-
-  public void fromDelta(DataInput in) throws IOException, InvalidDeltaException {
-    // Read whether to apply the changes to another DS if necessary
-    this.applyRemotely = in.readBoolean();
-
-    // Read the events
-    List<DeltaSessionAttributeEvent> events = null;
-    try {
-      events = DataSerializer.readArrayList(in);
-    } catch (ClassNotFoundException e) {
-      throw new InvalidDeltaException(e);
-    }
-
-    // This allows for backwards compatibility with 2.1 clients
-    if (((InputStream) in).available() > 0) {
-      this.lastAccessedTime = in.readLong();
-      this.maxInactiveInterval = in.readInt();
-    }
-
-    // Iterate and apply the events
-    for (DeltaSessionAttributeEvent event : events) {
-      event.apply(this);
-    }
-
-    // Add the events to the gateway delta region if necessary
-    if (this.enableGatewayDeltaReplication && this.applyRemotely) {
-      setCurrentGatewayDeltaEvent(
-          new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events));
-    }
-
-    // Access it to set the last accessed time. End access it to set not new.
-    access();
-    endAccess();
-  }
-
-  @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.writeObject(getSerializedAttributes(), out);
-    DataSerializer.writeByteArray(getSerializedPrincipal(), out);
-
-    // Write the DeltaSession state
-    out.writeBoolean(this.enableGatewayDeltaReplication);
-    DataSerializer.writeString(this.sessionRegionName, out);
-
-    DataSerializer.writeString(this.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();
-    this.attributes = readInAttributes(in);
-    this.serializedPrincipal = DataSerializer.readByteArray(in);
-
-    // Read the DeltaSession state
-    this.enableGatewayDeltaReplication = in.readBoolean();
-    this.sessionRegionName = DataSerializer.readString(in);
-
-    // This allows for backwards compatibility with 2.1 clients
-    if (((InputStream) in).available() > 0) {
-      this.contextName = DataSerializer.readString(in);
-    }
-
-    // Initialize the transients if necessary
-    if (this.listeners == null) {
-      this.listeners = new ArrayList<>();
-    }
-
-    if (this.notes == null) {
-      this.notes = new Hashtable<>();
-    }
-  }
-
-  protected ConcurrentMap<String, Object> readInAttributes(final DataInput in)
-      throws IOException, ClassNotFoundException {
-    return DataSerializer.readObject(in);
-  }
-
-  @Override
-  public int getSizeInBytes() {
-    int size = 0;
-    for (Enumeration<String> e = getAttributeNames(); e.hasMoreElements();) {
-      // Don't use this.getAttribute() because we don't want to deserialize
-      // the value.
-      Object value = super.getAttribute(e.nextElement());
-      if (value instanceof byte[]) {
-        size += ((byte[]) value).length;
-      }
-    }
-
-    return size;
-  }
-
-  @SuppressWarnings({"unchecked", "rawtypes"})
-  protected ConcurrentMap<String, byte[]> getSerializedAttributes() {
-    // Iterate the values and serialize them if necessary before sending them to the server. This
-    // makes the application classes unnecessary on the server.
-    ConcurrentMap<String, byte[]> serializedAttributes = new ConcurrentHashMap<String, byte[]>();
-    for (Iterator i = this.attributes.entrySet().iterator(); i.hasNext();) {
-      Map.Entry<String, Object> entry = (Map.Entry<String, Object>) i.next();
-      Object value = entry.getValue();
-      byte[] serializedValue = value instanceof byte[] ? (byte[]) value : serialize(value);
-      serializedAttributes.put(entry.getKey(), serializedValue);
-    }
-    return serializedAttributes;
-  }
-
-  protected byte[] serialize(Object obj) {
-    byte[] serializedValue = null;
-    try {
-      serializedValue = BlobHelper.serializeToBlob(obj);
-    } catch (IOException e) {
-      StringBuilder builder = new StringBuilder();
-      builder.append(this).append(": Object ").append(obj)
-          .append(" cannot be serialized due to the following exception");
-      ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-    }
-    return serializedValue;
-  }
-
-  @Override
-  public String toString() {
-    return new StringBuilder().append("DeltaSession[").append("id=").append(getId())
-        .append("; context=").append(this.contextName).append("; sessionRegionName=")
-        .append(this.sessionRegionName).append("; operatingRegionName=")
-        .append(getOperatingRegion() == null ? "unset" : getOperatingRegion().getFullPath())
-        .append("]").toString();
-  }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession8.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession8.java b/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession8.java
index f69382a..f2e797e 100644
--- a/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession8.java
+++ b/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession8.java
@@ -14,83 +14,17 @@
  */
 package org.apache.geode.modules.session.catalina;
 
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.servlet.http.HttpSession;
-
 import org.apache.catalina.Manager;
-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.juli.logging.LogFactory;
-
-import org.apache.geode.DataSerializable;
-import org.apache.geode.DataSerializer;
-import org.apache.geode.Delta;
-import org.apache.geode.InvalidDeltaException;
-import org.apache.geode.cache.Region;
-import org.apache.geode.internal.cache.lru.Sizeable;
-import org.apache.geode.internal.util.BlobHelper;
-import org.apache.geode.modules.gatewaydelta.GatewayDelta;
-import org.apache.geode.modules.gatewaydelta.GatewayDeltaEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionAttributeEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionAttributeEventBatch;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionDestroyAttributeEvent;
-import org.apache.geode.modules.session.catalina.internal.DeltaSessionUpdateAttributeEvent;
 
 
 @SuppressWarnings("serial")
-public class DeltaSession8 extends StandardSession
-    implements DataSerializable, Delta, GatewayDelta, Sizeable, DeltaSessionInterface {
-
-  private transient Region<String, HttpSession> operatingRegion;
-
-  private String sessionRegionName;
-
-  private String contextName;
-
-  private boolean hasDelta;
-
-  private boolean applyRemotely;
-
-  private boolean enableGatewayDeltaReplication;
-
-  private transient final Object changeLock = new Object();
-
-  private final List<DeltaSessionAttributeEvent> eventQueue =
-      new ArrayList<DeltaSessionAttributeEvent>();
-
-  private transient GatewayDeltaEvent currentGatewayDeltaEvent;
-
-  private transient boolean expired = false;
-
-  private transient boolean preferDeserializedForm = true;
-
-  private byte[] serializedPrincipal;
-
-  private final Log LOG = LogFactory.getLog(DeltaSession.class.getName());
-
+public class DeltaSession8 extends DeltaSession {
   /**
    * Construct a new <code>Session</code> associated with no <code>Manager</code>. The
    * <code>Manager</code> will be assigned later using {@link #setOwner(Object)}.
    */
   public DeltaSession8() {
-    super(null);
+    super();
   }
 
   /**
@@ -100,506 +34,6 @@ public class DeltaSession8 extends StandardSession
    */
   public DeltaSession8(Manager manager) {
     super(manager);
-    setOwner(manager);
-  }
-
-  /**
-   * Return the <code>HttpSession</code> for which this object is the facade.
-   */
-  @SuppressWarnings("unchecked")
-  public HttpSession getSession() {
-    if (facade == null) {
-      if (SecurityUtil.isPackageProtectionEnabled()) {
-        final DeltaSession8 fsession = this;
-        facade = (DeltaSessionFacade) AccessController.doPrivileged(new PrivilegedAction() {
-          public Object run() {
-            return new DeltaSessionFacade(fsession);
-          }
-        });
-      } else {
-        facade = new DeltaSessionFacade(this);
-      }
-    }
-    return (facade);
-  }
-
-  public Principal getPrincipal() {
-    if (this.principal == null && this.serializedPrincipal != null) {
-
-      Principal sp = null;
-      try {
-        sp = (Principal) BlobHelper.deserializeBlob(this.serializedPrincipal);
-      } catch (Exception e) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(this).append(
-            ": Serialized principal contains a byte[] that cannot be deserialized due to the following exception");
-        ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-        return null;
-      }
-      this.principal = sp;
-      if (getManager() != null) {
-        DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-        if (mgr.getLogger().isDebugEnabled()) {
-          mgr.getLogger().debug(this + ": Deserialized principal: " + this.principal);
-        }
-      }
-    }
-    return this.principal;
-  }
-
-  public void setPrincipal(Principal principal) {
-    super.setPrincipal(principal);
-
-    // Put the session into the region to serialize the principal
-    if (getManager() != null) {
-      getManager().add(this);
-      DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-      if (mgr.getLogger().isDebugEnabled()) {
-        mgr.getLogger().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;
-        this.serializedPrincipal = serialize(gp);
-        if (manager != null) {
-          DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-          if (mgr.getLogger().isDebugEnabled()) {
-            mgr.getLogger().debug(this + ": Serialized principal: " + gp);
-          }
-        }
-      }
-    }
-    return this.serializedPrincipal;
-  }
-
-  protected 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;
-  }
-
-  public boolean isCommitEnabled() {
-    DeltaSessionManager mgr = (DeltaSessionManager) getManager();
-    return mgr.isCommitValveEnabled();
-  }
-
-  public GatewayDeltaEvent getCurrentGatewayDeltaEvent() {
-    return this.currentGatewayDeltaEvent;
-  }
-
-  public void setCurrentGatewayDeltaEvent(GatewayDeltaEvent currentGatewayDeltaEvent) {
-    this.currentGatewayDeltaEvent = currentGatewayDeltaEvent;
-  }
-
-  @SuppressWarnings("unchecked")
-  public void setOwner(Object manager) {
-    if (manager instanceof DeltaSessionManager) {
-      DeltaSessionManager sessionManager = (DeltaSessionManager) manager;
-      this.manager = sessionManager;
-      initializeRegion(sessionManager);
-      this.hasDelta = false;
-      this.applyRemotely = false;
-      this.enableGatewayDeltaReplication = sessionManager.getEnableGatewayDeltaReplication();
-      this.preferDeserializedForm = sessionManager.getPreferDeserializedForm();
-
-      // Initialize transient variables
-      if (this.listeners == null) {
-        this.listeners = new ArrayList();
-      }
-
-      if (this.notes == null) {
-        this.notes = new Hashtable();
-      }
-
-      contextName = ((DeltaSessionManager) manager).getContextName();
-    } else {
-      throw new IllegalArgumentException(this + ": The Manager must be an AbstractManager");
-    }
-  }
-
-  private void checkBackingCacheAvailable() {
-    if (!((SessionManager) getManager()).isBackingCacheAvailable()) {
-      throw new IllegalStateException("No backing cache server is available.");
-    }
-  }
-
-  public void setAttribute(String name, Object value, boolean notify) {
-    checkBackingCacheAvailable();
-    synchronized (this.changeLock) {
-      // Serialize the value
-      byte[] serializedValue = serialize(value);
-
-      // Store the attribute locally
-      if (this.preferDeserializedForm) {
-        super.setAttribute(name, value, true);
-      } else {
-        super.setAttribute(name, serializedValue, true);
-      }
-
-      if (serializedValue == null) {
-        return;
-      }
-
-      // Create the update attribute message
-      DeltaSessionAttributeEvent event =
-          new DeltaSessionUpdateAttributeEvent(name, serializedValue);
-      queueAttributeEvent(event, true);
-
-      // Distribute the update
-      if (!isCommitEnabled()) {
-        putInRegion(getOperatingRegion(), true, null);
-      }
-    }
-  }
-
-  public void removeAttribute(String name, boolean notify) {
-    checkBackingCacheAvailable();
-    if (expired) {
-      return;
-    }
-    synchronized (this.changeLock) {
-      // Remove the attribute locally
-      super.removeAttribute(name, true);
-
-      // Create the destroy attribute message
-      DeltaSessionAttributeEvent event = new DeltaSessionDestroyAttributeEvent(name);
-      queueAttributeEvent(event, true);
-
-      // Distribute the update
-      if (!isCommitEnabled()) {
-        putInRegion(getOperatingRegion(), true, null);
-      }
-    }
-  }
-
-  public Object getAttribute(String name) {
-    checkBackingCacheAvailable();
-    Object value = super.getAttribute(name);
-
-    // 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) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(this).append(": Attribute named ").append(name).append(
-            " contains a byte[] that cannot be deserialized due to the following exception");
-        ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-      }
-      if (this.preferDeserializedForm) {
-        localUpdateAttribute(name, value);
-      }
-    }
-
-    // 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;
-  }
-
-  public void invalidate() {
-    super.invalidate();
-    // getOperatingRegion().destroy(this.id, true); // already done in super (remove)
-    ((DeltaSessionManager) getManager()).getStatistics().incSessionsInvalidated();
-  }
-
-  public void processExpired() {
-    DeltaSessionManager manager = (DeltaSessionManager) getManager();
-    if (manager != null && manager.getLogger() != null && manager.getLogger().isDebugEnabled()) {
-      ((DeltaSessionManager) getManager()).getLogger().debug(this + ": Expired");
-    }
-
-    // Set expired (so region.destroy is not called again)
-    setExpired(true);
-
-    // Do expire processing
-    super.expire(true);
-
-    // Update statistics
-    if (manager != null) {
-      manager.getStatistics().incSessionsExpired();
-    }
-  }
-
-  @Override
-  public void expire(boolean notify) {
-    if (notify) {
-      getOperatingRegion().destroy(this.getId(), this);
-    } else {
-      super.expire(false);
-    }
-  }
-
-  public void setMaxInactiveInterval(int interval) {
-    super.setMaxInactiveInterval(interval);
-  }
-
-  public void localUpdateAttribute(String name, Object value) {
-    super.setAttribute(name, value, false); // don't do notification since this is a replication
-  }
-
-  public void localDestroyAttribute(String name) {
-    super.removeAttribute(name, false); // don't do notification since this is a replication
-  }
-
-  public void applyAttributeEvents(Region<String, DeltaSessionInterface> region,
-      List<DeltaSessionAttributeEvent> events) {
-    for (DeltaSessionAttributeEvent event : events) {
-      event.apply(this);
-      queueAttributeEvent(event, false);
-    }
-
-    putInRegion(region, false, true);
-  }
-
-  private void initializeRegion(DeltaSessionManager sessionManager) {
-    // Get the session region name
-    this.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();
-    if (sessionManager.getLogger().isDebugEnabled()) {
-      sessionManager.getLogger().debug(this + ": Set operating region: " + this.operatingRegion);
-    }
-  }
-
-  private void queueAttributeEvent(DeltaSessionAttributeEvent event,
-      boolean checkAddToCurrentGatewayDelta) {
-    // Add to current gateway delta if necessary
-    if (checkAddToCurrentGatewayDelta) {
-      // 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 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<DeltaSessionAttributeEvent>();
-          events.add(event);
-          this.currentGatewayDeltaEvent =
-              new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events);
-        }
-      }
-    }
-    this.eventQueue.add(event);
-  }
-
-  @SuppressWarnings("unchecked")
-  private void putInRegion(Region region, boolean applyRemotely, Object callbackArgument) {
-    this.hasDelta = true;
-    this.applyRemotely = applyRemotely;
-    region.put(this.id, this, callbackArgument);
-    this.eventQueue.clear();
-  }
-
-  public void commit() {
-    if (!isValidInternal())
-      throw new IllegalStateException("commit: Session " + getId() + " already invalidated");
-    // (STRING_MANAGER.getString("deltaSession.commit.ise", getId()));
-
-    synchronized (this.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()) {
-        setCurrentGatewayDeltaEvent(
-            new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, this.eventQueue));
-      }
-      this.hasDelta = true;
-      this.applyRemotely = true;
-      putInRegion(getOperatingRegion(), true, null);
-      this.eventQueue.clear();
-    }
-  }
-
-  public void abort() {
-    synchronized (this.changeLock) {
-      this.eventQueue.clear();
-    }
-  }
-
-  private void setExpired(boolean expired) {
-    this.expired = expired;
-  }
-
-  public boolean getExpired() {
-    return this.expired;
-  }
-
-  public String getContextName() {
-    return contextName;
-  }
-
-  public boolean hasDelta() {
-    return this.hasDelta;
-  }
-
-  public void toDelta(DataOutput out) throws IOException {
-    // Write whether to apply the changes to another DS if necessary
-    out.writeBoolean(this.applyRemotely);
-
-    // Write the events
-    DataSerializer.writeArrayList((ArrayList) this.eventQueue, out);
-
-    out.writeLong(this.lastAccessedTime);
-    out.writeInt(this.maxInactiveInterval);
-  }
-
-  public void fromDelta(DataInput in) throws IOException, InvalidDeltaException {
-    // Read whether to apply the changes to another DS if necessary
-    this.applyRemotely = in.readBoolean();
-
-    // Read the events
-    List<DeltaSessionAttributeEvent> events = null;
-    try {
-      events = DataSerializer.readArrayList(in);
-    } catch (ClassNotFoundException e) {
-      throw new InvalidDeltaException(e);
-    }
-
-    // This allows for backwards compatibility with 2.1 clients
-    if (((InputStream) in).available() > 0) {
-      this.lastAccessedTime = in.readLong();
-      this.maxInactiveInterval = in.readInt();
-    }
-
-    // Iterate and apply the events
-    for (DeltaSessionAttributeEvent event : events) {
-      event.apply(this);
-    }
-
-    // Add the events to the gateway delta region if necessary
-    if (this.enableGatewayDeltaReplication && this.applyRemotely) {
-      setCurrentGatewayDeltaEvent(
-          new DeltaSessionAttributeEventBatch(this.sessionRegionName, this.id, events));
-    }
-
-    // Access it to set the last accessed time. End access it to set not new.
-    access();
-    endAccess();
-  }
-
-  @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.writeObject(getSerializedAttributes(), out);
-    DataSerializer.writeByteArray(getSerializedPrincipal(), out);
-
-    // Write the DeltaSession state
-    out.writeBoolean(this.enableGatewayDeltaReplication);
-    DataSerializer.writeString(this.sessionRegionName, out);
-
-    DataSerializer.writeString(this.contextName, out);
-  }
-
-  @Override
-  @SuppressWarnings("unchecked")
-  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();
-    this.attributes = readInAttributes(in);
-    this.serializedPrincipal = DataSerializer.readByteArray(in);
-
-    // Read the DeltaSession state
-    this.enableGatewayDeltaReplication = in.readBoolean();
-    this.sessionRegionName = DataSerializer.readString(in);
-
-    // This allows for backwards compatibility with 2.1 clients
-    if (((InputStream) in).available() > 0) {
-      this.contextName = DataSerializer.readString(in);
-    }
-
-    // Initialize the transients if necessary
-    if (this.listeners == null) {
-      this.listeners = new ArrayList();
-    }
-
-    if (this.notes == null) {
-      this.notes = new Hashtable();
-    }
-  }
-
-  @Override
-  public int getSizeInBytes() {
-    int size = 0;
-    for (Enumeration<String> e = getAttributeNames(); e.hasMoreElements();) {
-      // Don't use this.getAttribute() because we don't want to deserialize
-      // the value.
-      Object value = super.getAttribute(e.nextElement());
-      if (value instanceof byte[]) {
-        size += ((byte[]) value).length;
-      }
-    }
-
-    return size;
-  }
-
-  protected byte[] serialize(Object obj) {
-    byte[] serializedValue = null;
-    try {
-      serializedValue = BlobHelper.serializeToBlob(obj);
-    } catch (IOException e) {
-      StringBuilder builder = new StringBuilder();
-      builder.append(this).append(": Object ").append(obj)
-          .append(" cannot be serialized due to the following exception");
-      ((DeltaSessionManager) getManager()).getLogger().warn(builder.toString(), e);
-    }
-    return serializedValue;
-  }
-
-  @Override
-  public String toString() {
-    return new StringBuilder().append("DeltaSession[").append("id=").append(getId())
-        .append("; context=").append(this.contextName).append("; sessionRegionName=")
-        .append(this.sessionRegionName).append("; operatingRegionName=")
-        .append(getOperatingRegion() == null ? "unset" : getOperatingRegion().getFullPath())
-        .append("]").toString();
-  }
-
-  @SuppressWarnings({"unchecked", "rawtypes"})
-  protected ConcurrentMap<String, byte[]> getSerializedAttributes() {
-    // Iterate the values and serialize them if necessary before sending them to the server. This
-    // makes the application classes unnecessary on the server.
-    ConcurrentMap<String, byte[]> serializedAttributes = new ConcurrentHashMap<String, byte[]>();
-    for (Iterator i = this.attributes.entrySet().iterator(); i.hasNext();) {
-      Map.Entry<String, Object> entry = (Map.Entry<String, Object>) i.next();
-      Object value = entry.getValue();
-      byte[] serializedValue = value instanceof byte[] ? (byte[]) value : serialize(value);
-      serializedAttributes.put(entry.getKey(), serializedValue);
-    }
-    return serializedAttributes;
-  }
-
-  protected ConcurrentMap readInAttributes(final DataInput in)
-      throws IOException, ClassNotFoundException {
-    return DataSerializer.readObject(in);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/extensions/geode-modules/src/main/java/org/apache/geode/modules/session/catalina/DeltaSession.java
----------------------------------------------------------------------
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 ac612da..27e5bce 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
@@ -40,6 +40,7 @@ import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.security.AccessController;
 import java.security.Principal;
 import java.security.PrivilegedAction;
@@ -80,8 +81,18 @@ public class DeltaSession extends StandardSession
 
   private byte[] serializedPrincipal;
 
+  private static Field cachedField = null;
+
   private final Log LOG = LogFactory.getLog(DeltaSession.class.getName());
 
+  static {
+    try {
+      cachedField = StandardSession.class.getDeclaredField("attributes");
+      cachedField.setAccessible(true);
+    } catch (NoSuchFieldException e) {
+      throw new IllegalStateException(e);
+    }
+  }
   /**
    * The string manager for this package.
    */
@@ -531,7 +542,7 @@ public class DeltaSession extends StandardSession
     this.maxInactiveInterval = in.readInt();
     this.isNew = in.readBoolean();
     this.isValid = in.readBoolean();
-    this.attributes = readInAttributes(in);
+    readInAttributes(in);
     this.serializedPrincipal = DataSerializer.readByteArray(in);
 
     // Read the DeltaSession state
@@ -553,8 +564,26 @@ public class DeltaSession extends StandardSession
     }
   }
 
-  protected Map readInAttributes(final DataInput in) throws IOException, ClassNotFoundException {
-    return DataSerializer.readObject(in);
+  private void readInAttributes(DataInput in) throws IOException, ClassNotFoundException {
+    ConcurrentHashMap map = (ConcurrentHashMap) DataSerializer.readObject(in);
+    try {
+      Field field = getAttributesFieldObject();
+      field.set(this, map);
+    } catch (IllegalAccessException e) {
+      logError(e);
+      throw new IllegalStateException(e);
+    }
+  }
+
+  protected Field getAttributesFieldObject() {
+    return cachedField;
+  }
+
+  protected void logError(Exception e) {
+    if (getManager() != null) {
+      DeltaSessionManager mgr = (DeltaSessionManager) getManager();
+      mgr.getLogger().error(e);
+    }
   }
 
   @Override
@@ -576,8 +605,8 @@ public class DeltaSession extends StandardSession
   protected Map<String, byte[]> getSerializedAttributes() {
     // Iterate the values and serialize them if necessary before sending them to the server. This
     // makes the application classes unnecessary on the server.
-    Map<String, byte[]> serializedAttributes = new ConcurrentHashMap<String, byte[]>();
-    for (Iterator i = this.attributes.entrySet().iterator(); i.hasNext();) {
+    Map<String, byte[]> serializedAttributes = new ConcurrentHashMap<>();
+    for (Iterator i = getAttributes().entrySet().iterator(); i.hasNext();) {
       Map.Entry<String, Object> entry = (Map.Entry<String, Object>) i.next();
       Object value = entry.getValue();
       byte[] serializedValue = value instanceof byte[] ? (byte[]) value : serialize(value);
@@ -586,6 +615,17 @@ public class DeltaSession extends StandardSession
     return serializedAttributes;
   }
 
+  protected Map getAttributes() {
+    try {
+      Field field = getAttributesFieldObject();
+      Map map = (Map) field.get(this);
+      return map;
+    } catch (IllegalAccessException e) {
+      logError(e);
+    }
+    throw new IllegalStateException("Unable to access attributes field");
+  }
+
   protected byte[] serialize(Object obj) {
     byte[] serializedValue = null;
     try {

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-assembly/build.gradle
----------------------------------------------------------------------
diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle
index a76afdc..e135675 100755
--- a/geode-assembly/build.gradle
+++ b/geode-assembly/build.gradle
@@ -97,6 +97,8 @@ dependencies {
     exclude module: 'spring-core'
     exclude module: 'commons-logging'
   }
+
+  testCompile project(':geode-old-versions')
 }
 
 sourceSets {
@@ -430,6 +432,7 @@ build.dependsOn installDist
 
 installDist.dependsOn ':extensions/geode-modules-assembly:dist'
 distributedTest.dependsOn ':extensions/session-testing-war:war'
+distributedTest.dependsOn ':geode-old-versions:build'
 
 /**Print the names of all jar files in a fileTree */
 def printJars(tree) {

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java
index 9d03417..f9bce0a 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java
@@ -65,7 +65,7 @@ public abstract class ContainerInstall {
 
   public static final String GEODE_BUILD_HOME = System.getenv("GEODE_HOME");
   public static final String DEFAULT_INSTALL_DIR = "/tmp/cargo_containers/";
-  private static final String DEFAULT_MODULE_LOCATION = GEODE_BUILD_HOME + "/tools/Modules/";
+  protected static final String DEFAULT_MODULE_LOCATION = GEODE_BUILD_HOME + "/tools/Modules/";
   public static final String DEFAULT_MODULE_EXTRACTION_DIR = "/tmp/cargo_modules/";
 
   /**
@@ -95,6 +95,11 @@ public abstract class ContainerInstall {
     }
   }
 
+  public ContainerInstall(String installDir, String downloadURL, ConnectionType connType,
+      String moduleName) throws IOException {
+    this(installDir, downloadURL, connType, moduleName, DEFAULT_MODULE_LOCATION);
+  }
+
   /**
    * Base class for handling downloading and configuring J2EE installations
    *
@@ -103,18 +108,17 @@ public abstract class ContainerInstall {
    * installations.
    *
    * Subclasses provide installation of specific containers.
-   *
+   * 
    * @param connType Enum representing the connection type of this installation (either client
    *        server or peer to peer)
    * @param moduleName The module name of the installation being setup (i.e. tomcat, appserver,
    *        etc.)
    */
   public ContainerInstall(String installDir, String downloadURL, ConnectionType connType,
-      String moduleName) throws IOException {
+      String moduleName, String geodeModuleLocation) throws IOException {
     this.connType = connType;
 
-    // Removes previous run stuff (modules, installs, etc.)
-    clearPreviousRuns();
+    clearPreviousInstall(installDir);
 
     logger.info("Installing container from URL " + downloadURL);
 
@@ -125,7 +129,7 @@ public abstract class ContainerInstall {
     // Set install home
     INSTALL_PATH = installer.getHome();
     // Find and extract the module path
-    MODULE_PATH = findAndExtractModule(moduleName);
+    MODULE_PATH = findAndExtractModule(geodeModuleLocation, moduleName);
     logger.info("Extracted module " + moduleName + " to " + MODULE_PATH);
     // Find the session testing war path
     WAR_FILE_PATH = findSessionTestingWar();
@@ -148,17 +152,12 @@ public abstract class ContainerInstall {
   /**
    * Cleans up the installation by deleting the extracted module and downloaded installation folders
    */
-  public void clearPreviousRuns() throws IOException {
-    File modulesFolder = new File(DEFAULT_MODULE_EXTRACTION_DIR);
-    File installsFolder = new File(DEFAULT_INSTALL_DIR);
-
-    // Remove default modules extraction from previous runs
-    if (modulesFolder.exists()) {
-      FileUtils.deleteDirectory(modulesFolder);
-    }
-    // Remove default installs from previous runs
-    if (installsFolder.exists()) {
-      FileUtils.deleteDirectory(installsFolder);
+  public void clearPreviousInstall(String installDir) throws IOException {
+    File installFolder = new File(installDir);
+    // Remove installs from previous runs in the same folder
+    if (installFolder.exists()) {
+      logger.info("Deleting previous install folder " + installFolder.getAbsolutePath());
+      FileUtils.deleteDirectory(installFolder);
     }
   }
 
@@ -256,7 +255,7 @@ public abstract class ContainerInstall {
 
   /**
    * Generates a {@link ServerContainer} from the given {@link ContainerInstall}
-   *
+   * 
    * @param containerDescriptors Additional descriptors used to identify a container
    */
   public abstract ServerContainer generateContainer(File containerConfigHome,
@@ -298,15 +297,15 @@ public abstract class ContainerInstall {
 
   /**
    * Finds and extracts the geode module associated with the specified module.
-   *
+   * 
    * @param moduleName The module name (i.e. tomcat, appserver, etc.) of the module that should be
    *        extract. Used as a search parameter to find the module archive.
    * @return The path to the non-archive (extracted) version of the module files
-   * @throws IOException
    */
-  protected static String findAndExtractModule(String moduleName) throws IOException {
+  protected static String findAndExtractModule(String geodeModuleLocation, String moduleName)
+      throws IOException {
     File modulePath = null;
-    File modulesDir = new File(DEFAULT_MODULE_LOCATION);
+    File modulesDir = new File(geodeModuleLocation);
 
     boolean archive = false;
     logger.info("Trying to access build dir " + modulesDir);
@@ -318,21 +317,28 @@ public abstract class ContainerInstall {
         modulePath = file;
 
         archive = !file.isDirectory();
-        if (!archive)
+        if (!archive) {
           break;
+        }
       }
     }
 
+    String extractedModulePath =
+        modulePath.getName().substring(0, modulePath.getName().length() - 4);
+    // Get the name of the new module folder within the extraction directory
+    File newModuleFolder = new File(DEFAULT_MODULE_EXTRACTION_DIR + extractedModulePath);
+    // Remove any previous module folders extracted here
+    if (newModuleFolder.exists()) {
+      logger.info("Deleting previous modules directory " + newModuleFolder.getAbsolutePath());
+      FileUtils.deleteDirectory(newModuleFolder);
+    }
+
     // Unzip if it is a zip file
     if (archive) {
       if (!FilenameUtils.getExtension(modulePath.getAbsolutePath()).equals("zip")) {
         throw new IOException("Bad module archive " + modulePath);
       }
 
-      // Get the name of the new module folder within the extraction directory
-      File newModuleFolder = new File(DEFAULT_MODULE_EXTRACTION_DIR
-          + modulePath.getName().substring(0, modulePath.getName().length() - 4));
-
       // Extract folder to location if not already there
       if (!newModuleFolder.exists()) {
         ZipUtils.unzip(modulePath.getAbsolutePath(), newModuleFolder.getAbsolutePath());
@@ -342,14 +348,15 @@ public abstract class ContainerInstall {
     }
 
     // No module found within directory throw IOException
-    if (modulePath == null)
+    if (modulePath == null) {
       throw new IOException("No module found in " + modulesDir);
+    }
     return modulePath.getAbsolutePath();
   }
 
   /**
    * Edits the specified property within the given property file
-   *
+   * 
    * @param filePath path to the property file
    * @param propertyName property name to edit
    * @param propertyValue new property value
@@ -364,10 +371,11 @@ public abstract class ContainerInstall {
     properties.load(input);
 
     String val;
-    if (append)
+    if (append) {
       val = properties.getProperty(propertyName) + propertyValue;
-    else
+    } else {
       val = propertyValue;
+    }
 
     properties.setProperty(propertyName, val);
     properties.store(new FileOutputStream(filePath), null);
@@ -397,7 +405,7 @@ public abstract class ContainerInstall {
    * {@link #rewriteNodeAttributes(Node, HashMap)},
    * {@link #nodeHasExactAttributes(Node, HashMap, boolean)} to edit the required parts of the XML
    * file.
-   *
+   * 
    * @param XMLPath The path to the xml file to edit
    * @param tagId The id of tag to edit. If null, then this method will add a new xml element,
    *        unless writeOnSimilarAttributeNames is set to true.
@@ -441,11 +449,13 @@ public abstract class ContainerInstall {
       } else {
         Element e = doc.createElement(tagName);
         // Set id attribute
-        if (tagId != null)
+        if (tagId != null) {
           e.setAttribute("id", tagId);
+        }
         // Set other attributes
-        for (String key : attributes.keySet())
+        for (String key : attributes.keySet()) {
           e.setAttribute(key, attributes.get(key));
+        }
 
         // Add it as a child of the tag for the file
         doc.getElementsByTagName(parentTagName).item(0).appendChild(e);
@@ -466,7 +476,7 @@ public abstract class ContainerInstall {
 
   /**
    * Finds the node in the given document with the given name and attribute
-   *
+   * 
    * @param doc XML document to search for the node
    * @param nodeName The name of the node to search for
    * @param name The name of the attribute that the node should contain
@@ -476,15 +486,17 @@ public abstract class ContainerInstall {
   private static Node findNodeWithAttribute(Document doc, String nodeName, String name,
       String value) {
     NodeList nodes = doc.getElementsByTagName(nodeName);
-    if (nodes == null)
+    if (nodes == null) {
       return null;
+    }
 
     for (int i = 0; i < nodes.getLength(); i++) {
       Node node = nodes.item(i);
       Node nodeAttr = node.getAttributes().getNamedItem(name);
 
-      if (nodeAttr != null && nodeAttr.getTextContent().equals(value))
+      if (nodeAttr != null && nodeAttr.getTextContent().equals(value)) {
         return node;
+      }
     }
 
     return null;
@@ -492,7 +504,7 @@ public abstract class ContainerInstall {
 
   /**
    * Replaces the node's attributes with the attributes in the given hashmap
-   *
+   * 
    * @param node XML node that should be edited
    * @param attributes HashMap of strings representing the attributes of a node (key = value)
    * @return The given node with ONLY the given attributes
@@ -501,12 +513,14 @@ public abstract class ContainerInstall {
     NamedNodeMap nodeAttrs = node.getAttributes();
 
     // Remove all previous attributes
-    while (nodeAttrs.getLength() > 0)
+    while (nodeAttrs.getLength() > 0) {
       nodeAttrs.removeNamedItem(nodeAttrs.item(0).getNodeName());
+    }
 
     // Set to new attributes
-    for (String key : attributes.keySet())
+    for (String key : attributes.keySet()) {
       ((Element) node).setAttribute(key, attributes.get(key));
+    }
 
     return node;
   }
@@ -514,7 +528,7 @@ public abstract class ContainerInstall {
   /**
    * Checks to see whether the given XML node has the exact attributes given in the attributes
    * hashmap
-   *
+   * 
    * @param checkSimilarValues If true, will also check to make sure that the given node's
    *        attributes also have the exact same values as the ones given in the attributes HashMap.
    * @return True if the node has only the attributes the are given by the HashMap (no more and no

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatInstall.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatInstall.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatInstall.java
index ba5f6bc..57dc519 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatInstall.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatInstall.java
@@ -32,6 +32,8 @@ import java.util.regex.Pattern;
  */
 public class TomcatInstall extends ContainerInstall {
 
+  public static final String GEODE_BUILD_HOME_LIB = GEODE_BUILD_HOME + "/lib/";
+
   /**
    * Version of tomcat that this class will install
    *
@@ -43,6 +45,10 @@ public class TomcatInstall extends ContainerInstall {
         "http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.37/bin/apache-tomcat-6.0.37.zip"),
     TOMCAT7(7,
         "http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.73/bin/apache-tomcat-7.0.73.zip"),
+    TOMCAT755(7,
+        "http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.55/bin/apache-tomcat-7.0.55.zip"),
+    TOMCAT779(7,
+        "http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.79/bin/apache-tomcat-7.0.79.zip"),
     TOMCAT8(8,
         "http://archive.apache.org/dist/tomcat/tomcat-8/v8.5.15/bin/apache-tomcat-8.5.15.zip"),
     TOMCAT9(9,
@@ -72,7 +78,7 @@ public class TomcatInstall extends ContainerInstall {
         case TOMCAT9:
           return 8;
         default:
-          throw new IllegalArgumentException("Illegal tomcat version option");
+          return getVersion();
       }
     }
 
@@ -93,6 +99,8 @@ public class TomcatInstall extends ContainerInstall {
         case TOMCAT6:
           return null;
         case TOMCAT7:
+        case TOMCAT755:
+        case TOMCAT779:
           return "tomcat.util.scan.DefaultJarScanner.jarsToSkip";
         case TOMCAT8:
         case TOMCAT9:
@@ -104,42 +112,40 @@ public class TomcatInstall extends ContainerInstall {
   }
 
   private static final String[] tomcatRequiredJars =
-      {"antlr", "commons-lang", "fastutil", "geode-core", "geode-modules", "geode-modules-tomcat7",
-          "geode-modules-tomcat8", "javax.transaction-api", "jgroups", "log4j-api", "log4j-core",
-          "log4j-jul", "shiro-core", "slf4j-api", "slf4j-jdk14", "commons-validator"};
+      {"antlr", "commons-lang", "fastutil", "geode-core", "javax.transaction-api", "jgroups",
+          "log4j-api", "log4j-core", "log4j-jul", "shiro-core", "commons-validator"};
 
   private final TomcatVersion version;
 
-  public TomcatInstall(TomcatVersion version) throws Exception {
-    this(version, ConnectionType.PEER_TO_PEER, DEFAULT_INSTALL_DIR);
-  }
-
   public TomcatInstall(TomcatVersion version, String installDir) throws Exception {
-    this(version, ConnectionType.PEER_TO_PEER, installDir);
+    this(version, ConnectionType.PEER_TO_PEER, installDir, DEFAULT_MODULE_LOCATION,
+        GEODE_BUILD_HOME_LIB);
   }
 
-  public TomcatInstall(TomcatVersion version, ConnectionType connType) throws Exception {
-    this(version, connType, DEFAULT_INSTALL_DIR);
+  public TomcatInstall(TomcatVersion version, ConnectionType connType, String installDir)
+      throws Exception {
+    this(version, connType, installDir, DEFAULT_MODULE_LOCATION, GEODE_BUILD_HOME_LIB);
   }
 
   /**
    * Download and setup an installation tomcat using the {@link ContainerInstall} constructor and
    * some extra functions this class provides
    *
-   * Specifically, this function uses {@link #copyTomcatGeodeReqFiles(String)} to install geode
-   * session into Tomcat, {@link #setupDefaultSettings()} to modify the context and server XML files
-   * within the installation's 'conf' folder, and {@link #updateProperties()} to set the jar
+   * Specifically, this function uses {@link #copyTomcatGeodeReqFiles(String, String)} to install
+   * geode session into Tomcat, {@link #setupDefaultSettings()} to modify the context and server XML
+   * files within the installation's 'conf' folder, and {@link #updateProperties()} to set the jar
    * skipping properties needed to speedup container startup.
    */
-  public TomcatInstall(TomcatVersion version, ConnectionType connType, String installDir)
-      throws Exception {
+  public TomcatInstall(TomcatVersion version, ConnectionType connType, String installDir,
+      String modulesJarLocation, String extraJarsPath) throws Exception {
     // Does download and install from URL
-    super(installDir, version.getDownloadURL(), connType, "tomcat");
+    super(installDir, version.getDownloadURL(), connType, "tomcat", modulesJarLocation);
 
     this.version = version;
+    modulesJarLocation = getModulePath() + "/lib/";
 
     // Install geode sessions into tomcat install
-    copyTomcatGeodeReqFiles(GEODE_BUILD_HOME + "/lib/");
+    copyTomcatGeodeReqFiles(modulesJarLocation, extraJarsPath);
     // Set some default XML attributes in server and cache XMLs
     setupDefaultSettings();
 
@@ -255,39 +261,26 @@ public class TomcatInstall extends ContainerInstall {
    * @throws IOException if the {@link #getModulePath()}, installation lib directory, or extra
    *         directory passed in contain no files.
    */
-  private void copyTomcatGeodeReqFiles(String extraJarsPath) throws IOException {
+  private void copyTomcatGeodeReqFiles(String moduleJarDir, String extraJarsPath)
+      throws IOException {
     ArrayList<File> requiredFiles = new ArrayList<>();
     // The library path for the current tomcat installation
     String tomcatLibPath = getHome() + "/lib/";
 
     // List of required jars and form version regexps from them
-    String versionRegex = "-[0-9]+.*\\.jar";
+    String versionRegex = "-?[0-9]*.*\\.jar";
     ArrayList<Pattern> patterns = new ArrayList<>(tomcatRequiredJars.length);
     for (String jar : tomcatRequiredJars)
       patterns.add(Pattern.compile(jar + versionRegex));
 
     // Don't need to copy any jars already in the tomcat install
     File tomcatLib = new File(tomcatLibPath);
-    if (tomcatLib.exists()) {
-      try {
-        for (File file : tomcatLib.listFiles())
-          patterns.removeIf(pattern -> pattern.matcher(file.getName()).find());
-      } catch (NullPointerException e) {
-        throw new IOException("No files found in tomcat lib directory " + tomcatLibPath);
-      }
-    } else {
-      tomcatLib.mkdir();
-    }
 
     // Find all the required jars in the tomcatModulePath
     try {
-      for (File file : (new File(getModulePath() + "/lib/")).listFiles()) {
-        for (Pattern pattern : patterns) {
-          if (pattern.matcher(file.getName()).find()) {
-            requiredFiles.add(file);
-            patterns.remove(pattern);
-            break;
-          }
+      for (File file : (new File(moduleJarDir)).listFiles()) {
+        if (file.isFile() && file.getName().endsWith(".jar")) {
+          requiredFiles.add(file);
         }
       }
     } catch (NullPointerException e) {
@@ -301,7 +294,6 @@ public class TomcatInstall extends ContainerInstall {
         for (Pattern pattern : patterns) {
           if (pattern.matcher(file.getName()).find()) {
             requiredFiles.add(file);
-            patterns.remove(pattern);
             break;
           }
         }

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatSessionBackwardsCompatibilityTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatSessionBackwardsCompatibilityTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatSessionBackwardsCompatibilityTest.java
new file mode 100644
index 0000000..7b23380
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/TomcatSessionBackwardsCompatibilityTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.standalone.VersionManager;
+import org.apache.geode.test.junit.categories.BackwardCompatibilityTest;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+/**
+ * This test iterates through the versions of Geode and executes session client compatibility with
+ * the current version of Geode.
+ */
+@Category({DistributedTest.class, BackwardCompatibilityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class TomcatSessionBackwardsCompatibilityTest {
+
+  @Parameterized.Parameters
+  public static Collection<String> data() {
+    List<String> result = VersionManager.getInstance().getVersionsWithoutCurrent();
+    result.removeIf(s -> Integer.parseInt(s) < 120);
+    if (result.size() < 1) {
+      throw new RuntimeException("No older versions of Geode were found to test against");
+    }
+    return result;
+  }
+
+  @Rule
+  public transient GfshShellConnectionRule gfsh = new GfshShellConnectionRule();
+
+  @Rule
+  public transient LocatorServerStartupRule locatorStartup = new LocatorServerStartupRule();
+
+  @Rule
+  public transient TestName testName = new TestName();
+
+  public transient Client client;
+  public transient ContainerManager manager;
+
+  File oldBuild;
+  File oldModules;
+
+  TomcatInstall tomcat7079AndOldModules;
+  TomcatInstall tomcat7079AndCurrentModules;
+  TomcatInstall tomcat8AndOldModules;
+  TomcatInstall tomcat8AndCurrentModules;
+
+  int locatorPort;
+  String classPathTomcat7079;
+  String classPathTomcat8;
+
+  public TomcatSessionBackwardsCompatibilityTest(String version) {
+    VersionManager versionManager = VersionManager.getInstance();
+    String installLocation = versionManager.getInstall(version);
+    oldBuild = new File(installLocation);
+    oldModules = new File(installLocation + "/tools/Modules/");
+  }
+
+  protected void startServer(String name, String classPath, int locatorPort) throws Exception {
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
+    command.addOption(CliStrings.START_SERVER__NAME, name);
+    command.addOption(CliStrings.START_SERVER__SERVER_PORT, "0");
+    command.addOption(CliStrings.START_SERVER__CLASSPATH, classPath);
+    command.addOption(CliStrings.START_SERVER__LOCATORS, "localhost[" + locatorPort + "]");
+    gfsh.executeAndVerifyCommand(command.toString());
+  }
+
+  protected void startLocator(String name, String classPath, int port) throws Exception {
+    CommandStringBuilder locStarter = new CommandStringBuilder(CliStrings.START_LOCATOR);
+    locStarter.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, name);
+    locStarter.addOption(CliStrings.START_LOCATOR__CLASSPATH, classPath);
+    locStarter.addOption(CliStrings.START_LOCATOR__PORT, Integer.toString(port));
+    gfsh.executeAndVerifyCommand(locStarter.toString());
+
+  }
+
+  @Before
+  public void setup() throws Exception {
+    tomcat7079AndOldModules = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT779,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat7079AndOldModules",
+        oldModules.getAbsolutePath(), oldBuild.getAbsolutePath() + "/lib");
+
+    tomcat7079AndCurrentModules = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT779,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat7079AndCurrentModules");
+
+    tomcat8AndOldModules = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT8,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat8AndOldModules", oldModules.getAbsolutePath(),
+        oldBuild.getAbsolutePath() + "/lib");
+
+    tomcat8AndCurrentModules = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT8,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat8AndCurrentModules");
+
+    classPathTomcat7079 = tomcat7079AndCurrentModules.getHome() + "/lib/*" + File.pathSeparator
+        + tomcat7079AndCurrentModules.getHome() + "/bin/*";
+    classPathTomcat8 = tomcat8AndCurrentModules.getHome() + "/lib/*" + File.pathSeparator
+        + tomcat8AndCurrentModules.getHome() + "/bin/*";
+
+    // Get available port for the locator
+    locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+
+    tomcat7079AndOldModules.setDefaultLocator("localhost", locatorPort);
+    tomcat7079AndCurrentModules.setDefaultLocator("localhost", locatorPort);
+
+    tomcat8AndOldModules.setDefaultLocator("localhost", locatorPort);
+    tomcat8AndCurrentModules.setDefaultLocator("localhost", locatorPort);
+
+    client = new Client();
+    manager = new ContainerManager();
+    // Due to parameterization of the test name, the URI would be malformed. Instead, it strips off
+    // the [] symbols
+    manager.setTestName(testName.getMethodName().replace("[", "").replace("]", ""));
+  }
+
+  private void startClusterWithTomcat(String tomcatClassPath) throws Exception {
+    startLocator("loc", tomcatClassPath, locatorPort);
+    startServer("server", tomcatClassPath, locatorPort);
+  }
+
+  /**
+   * Stops all containers that were previously started and cleans up their configurations
+   */
+  @After
+  public void stop() throws Exception {
+    manager.stopAllActiveContainers();
+    manager.cleanUp();
+
+    CommandStringBuilder locStop = new CommandStringBuilder(CliStrings.STOP_LOCATOR);
+    locStop.addOption(CliStrings.STOP_LOCATOR__DIR, "loc");
+    gfsh.executeAndVerifyCommand(locStop.toString());
+
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.STOP_SERVER);
+    command.addOption(CliStrings.STOP_SERVER__DIR, "server");
+    gfsh.executeAndVerifyCommand(command.toString());
+  }
+
+  private void doPutAndGetSessionOnAllClients() throws IOException, URISyntaxException {
+    // This has to happen at the start of every test
+    manager.startAllInactiveContainers();
+
+    String key = "value_testSessionPersists";
+    String value = "Foo";
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response resp = client.set(key, value);
+    String cookie = resp.getSessionCookie();
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      System.out.println("Checking get for container:" + i);
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals("Session data is not replicating properly", value, resp.getResponse());
+    }
+  }
+
+  @Test
+  public void tomcat7079WithOldModuleCanDoPuts() throws Exception {
+    startClusterWithTomcat(classPathTomcat7079);
+    manager.addContainer(tomcat7079AndOldModules);
+    manager.addContainer(tomcat7079AndOldModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+  @Test
+  public void tomcat7079WithOldModulesMixedWithCurrentCanDoPutFromOldModule() throws Exception {
+    startClusterWithTomcat(classPathTomcat7079);
+    manager.addContainer(tomcat7079AndOldModules);
+    manager.addContainer(tomcat7079AndCurrentModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+  @Test
+  public void tomcat7079WithOldModulesMixedWithCurrentCanDoPutFromCurrentModule() throws Exception {
+    startClusterWithTomcat(classPathTomcat7079);
+    manager.addContainer(tomcat7079AndCurrentModules);
+    manager.addContainer(tomcat7079AndOldModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+  @Test
+  public void tomcat8WithOldModuleCanDoPuts() throws Exception {
+    startClusterWithTomcat(classPathTomcat8);
+    manager.addContainer(tomcat8AndOldModules);
+    manager.addContainer(tomcat8AndOldModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+  @Test
+  public void tomcat8WithOldModulesMixedWithCurrentCanDoPutFromOldModule() throws Exception {
+    startClusterWithTomcat(classPathTomcat8);
+    manager.addContainer(tomcat8AndOldModules);
+    manager.addContainer(tomcat8AndCurrentModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+  @Test
+  public void tomcat8WithOldModulesMixedWithCurrentCanDoPutFromCurrentModule() throws Exception {
+    startClusterWithTomcat(classPathTomcat8);
+    manager.addContainer(tomcat8AndCurrentModules);
+    manager.addContainer(tomcat8AndOldModules);
+    doPutAndGetSessionOnAllClients();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManager.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManager.java b/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManager.java
index 8eefa01..9f4c357 100755
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManager.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManager.java
@@ -24,7 +24,9 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
+import java.util.function.BiConsumer;
 
 /**
  * VersionManager loads the class-paths for all of the releases of Geode configured for
@@ -44,7 +46,11 @@ public class VersionManager {
   protected static void init() {
     instance = new VersionManager();
     final String fileName = "geodeOldVersionClasspaths.txt";
+    final String installLocations = "geodeOldVersionInstalls.txt";
     instance.findVersions(fileName);
+    instance.findInstalls(installLocations);
+    System.out
+        .println("VersionManager has loaded the following classpaths:\n" + instance.classPaths);
   }
 
   public static VersionManager getInstance() {
@@ -58,7 +64,7 @@ public class VersionManager {
    * for unit testing, this creates a VersionManager with paths loaded from the given file, which
    * may or may not exist. The instance is not retained
    */
-  protected static VersionManager getInstance(String classpathsFileName) {
+  protected static VersionManager getInstance(String classpathsFileName, String installFileName) {
     VersionManager result = new VersionManager();
     result.findVersions(classpathsFileName);
     return result;
@@ -71,6 +77,8 @@ public class VersionManager {
 
   private List<String> testVersions = new ArrayList<String>(10);
 
+  private Map<String, String> installs = new HashMap();
+
   /**
    * Test to see if a version string is known to VersionManager. Versions are either CURRENT_VERSION
    * or one of the versions returned by VersionManager#getVersions()
@@ -94,6 +102,11 @@ public class VersionManager {
     return classPaths.get(version);
   }
 
+
+  public String getInstall(String version) {
+    return installs.get(version);
+  }
+
   /**
    * Returns a list of older versions available for testing
    */
@@ -118,30 +131,57 @@ public class VersionManager {
 
   private void findVersions(String fileName) {
     // this file is created by the gradle task createClasspathsPropertiesFile
+    readVersionsFile(fileName, (version, path) -> {
+      Optional<String> parsedVersion = parseVersion(version);
+      if (parsedVersion.isPresent()) {
+        classPaths.put(parsedVersion.get(), path);
+        testVersions.add(parsedVersion.get());
+      }
+    });
+  }
+
+  private void findInstalls(String fileName) {
+    readVersionsFile(fileName, (version, install) -> {
+      Optional<String> parsedVersion = parseVersion(version);
+      if (parsedVersion.isPresent()) {
+        installs.put(parsedVersion.get(), install);
+      }
+    });
+  }
+
+  private Optional<String> parseVersion(String version) {
+    String parsedVersion = null;
+    if (version.startsWith("test") && version.length() >= "test".length()) {
+      if (version.equals("test")) {
+        parsedVersion = CURRENT_VERSION;
+      } else {
+        parsedVersion = version.substring("test".length());
+      }
+    }
+    return Optional.ofNullable(parsedVersion);
+  }
+
+  private void readVersionsFile(String fileName, BiConsumer<String, String> consumer) {
+    Properties props = readPropertiesFile(fileName);
+    props.forEach((k, v) -> {
+      consumer.accept(k.toString(), v.toString());
+    });
+  }
+
+  public Properties readPropertiesFile(String fileName) {
+    // this file is created by the gradle task createClasspathsPropertiesFile
     Properties props = new Properties();
     URL url = VersionManager.class.getResource("/" + fileName);
     if (url == null) {
       loadFailure = "VersionManager: unable to locate " + fileName + " in class-path";
-      return;
+      return props;
     }
     try (InputStream in = VersionManager.class.getResource("/" + fileName).openStream()) {
       props.load(in);
     } catch (IOException e) {
       loadFailure = "VersionManager: unable to read resource " + fileName;
-      return;
-    }
-
-    for (Map.Entry<Object, Object> entry : props.entrySet()) {
-      String version = (String) entry.getKey();
-      if (version.startsWith("test") && version.length() >= "test".length()) {
-        if (version.equals("test")) {
-          version = CURRENT_VERSION;
-        } else {
-          version = version.substring("test".length());
-        }
-        classPaths.put(version, (String) entry.getValue());
-        testVersions.add(version);
-      }
+      return props;
     }
+    return props;
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManagerJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManagerJUnitTest.java b/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManagerJUnitTest.java
index af1fa58..7e89dfc 100755
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManagerJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/standalone/VersionManagerJUnitTest.java
@@ -27,13 +27,15 @@ public class VersionManagerJUnitTest {
 
   @Test
   public void exceptionIsNotThrownInInitialization() throws Exception {
-    VersionManager instance = VersionManager.getInstance("--nonexistant-file?--");
+    VersionManager instance =
+        VersionManager.getInstance("--nonexistant-file?--", "--nonexistant-install-file--");
     Assert.assertNotEquals("", instance.loadFailure);
   }
 
   @Test
   public void exceptionIsThrownOnUse() throws Exception {
-    VersionManager instance = VersionManager.getInstance("--nonexistant-file?--");
+    VersionManager instance =
+        VersionManager.getInstance("--nonexistant-file?--", "--nonexistant-install-file--");
     Assert.assertNotEquals("", instance.loadFailure);
     assertThatThrownBy(() -> instance.getVersionsWithoutCurrent()).hasMessage(instance.loadFailure);
     assertThatThrownBy(() -> instance.getVersions()).hasMessage(instance.loadFailure);

http://git-wip-us.apache.org/repos/asf/geode/blob/f38dff9d/geode-old-versions/build.gradle
----------------------------------------------------------------------
diff --git a/geode-old-versions/build.gradle b/geode-old-versions/build.gradle
index 1a39ea0..2e9257c 100644
--- a/geode-old-versions/build.gradle
+++ b/geode-old-versions/build.gradle
@@ -15,11 +15,16 @@
  * limitations under the License.
  */
 
+plugins {
+  id "de.undercouch.download" version "3.2.0"
+}
 
+import de.undercouch.gradle.tasks.download.Download
 disableMavenPublishing()
 
-def addTestSource(def source, def geodeVersion) {
-//  def sourceSet =
+project.ext.installs = new Properties();
+
+def addOldVersion(def source, def geodeVersion, def downloadInstall) {
   sourceSets.create(source, {
     compileClasspath += configurations.provided
     runtimeClasspath += configurations.provided
@@ -33,14 +38,36 @@ def addTestSource(def source, def geodeVersion) {
   dependencies.add "${source}Compile", "org.apache.geode:geode-cq:$geodeVersion"
   dependencies.add "${source}Compile", "org.apache.geode:geode-rebalancer:$geodeVersion"
 
-}
+  project.ext.installs.setProperty(source, "$buildDir/apache-geode-${geodeVersion}")
 
-// Add sourceSets for backwards compatibility, rolling upgrade, and
-// pdx testing.
-addTestSource('test100', '1.0.0-incubating')
-addTestSource('test110', '1.1.0')
-addTestSource('test111', '1.1.1')
-addTestSource('test120', '1.2.0')
+  task "downloadZipFile${source}" (type: Download) {
+    src "https://www.apache.org/dyn/closer.cgi?action=download&filename=geode/$geodeVersion/apache-geode-${geodeVersion}.tar.gz"
+    dest new File(buildDir, "apache-geode-${geodeVersion}.tar.gz")
+  }
+
+  task "downloadSHA${source}" (type: Download) {
+    src "https://www.apache.org/dist/geode/${geodeVersion}/apache-geode-${geodeVersion}.tar.gz.sha256"
+    dest new File(buildDir, "apache-geode-${geodeVersion}.tar.gz.sha256")
+  }
+
+
+  task "verifyGeode${source}" (type: de.undercouch.gradle.tasks.download.Verify, dependsOn: [tasks["downloadSHA${source}"], tasks["downloadZipFile${source}"]]) {
+    src tasks["downloadZipFile${source}"].dest
+    algorithm "SHA-256"
+    doFirst {
+      checksum new File(buildDir, "apache-geode-${geodeVersion}.tar.gz.sha256").text.split(' ')[0]
+    }
+  }
+
+  task "downloadAndUnzipFile${source}" (dependsOn: "verifyGeode${source}", type: Copy) {
+    from tarTree(tasks["downloadZipFile${source}"].dest)
+    into buildDir
+  }
+
+  if (downloadInstall) {
+    createGeodeClasspathsFile.dependsOn tasks["downloadAndUnzipFile${source}"]
+  }
+}
 
 def generatedResources = "$buildDir/generated-resources/main"
 
@@ -52,7 +79,9 @@ sourceSets {
 
 task createGeodeClasspathsFile  {
   File classpathsFile = file("$generatedResources/geodeOldVersionClasspaths.txt")
-  outputs.file(classpathsFile);
+  File installsFile = file("$generatedResources/geodeOldVersionInstalls.txt")
+  outputs.file(classpathsFile)
+  outputs.file(installsFile)
 
   doLast {
     Properties versions = new Properties();
@@ -65,6 +94,21 @@ task createGeodeClasspathsFile  {
     new FileOutputStream(classpathsFile).withStream { fos ->
       versions.store(fos, '')
     }
+
+    installsFile.getParentFile().mkdirs();
+
+    new FileOutputStream(installsFile).withStream { fos ->
+      project.ext.installs.store(fos, '')
+    }
   }
+
+  // Add sourceSets for backwards compatibility, rolling upgrade, and
+  // pdx testing.
+  addOldVersion('test100', '1.0.0-incubating', false)
+  addOldVersion('test110', '1.1.0', false)
+  addOldVersion('test111', '1.1.1', false)
+  addOldVersion('test120', '1.2.0', true)
+
 }
 
+