geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ladyva...@apache.org
Subject [02/51] [abbrv] geode git commit: GEODE-2901: Resolving possible session testing concurrency issues
Date Wed, 19 Jul 2017 18:04:20 GMT
GEODE-2901: Resolving possible session testing concurrency issues

Updated installation and container management to allow more container independence.
Previously, containers edited files within the installation, leading to several containers modifying the same file. Given the concurrent nature of runs, this could become a problem if containers ever wrote something to the same part of each file.
Containers now create their own separate and unique files that they use to run, hopefully removing any concurrency problems that might arise.

Also tried to optimize for long term sanity of whoever must maintain this code.

This closes #619


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

Branch: refs/heads/feature/GEODE-3113
Commit: 6e86762385b14ab9a35a1f045251ea5a7411e23a
Parents: f78fc71
Author: David Anuta <david.r.anuta@gmail.com>
Authored: Mon Jul 3 16:55:03 2017 -0700
Committer: Dan Smith <upthewaterspout@apache.org>
Committed: Mon Jul 10 16:24:30 2017 -0700

----------------------------------------------------------------------
 .../geode/session/tests/CargoTestBase.java      |  29 +-
 .../geode/session/tests/ContainerInstall.java   | 418 ++++++++++++-------
 .../geode/session/tests/ContainerManager.java   | 334 +++++----------
 .../tests/GenericAppServerContainer.java        | 150 +++++++
 .../session/tests/GenericAppServerInstall.java  | 226 +++-------
 .../session/tests/Jetty9ClientServerTest.java   |   6 +-
 .../apache/geode/session/tests/Jetty9Test.java  |   5 +-
 .../geode/session/tests/ServerContainer.java    | 380 +++++++++++++++++
 .../session/tests/Tomcat6ClientServerTest.java  |   4 +-
 .../apache/geode/session/tests/Tomcat6Test.java |   2 +-
 .../session/tests/Tomcat7ClientServerTest.java  |   6 +-
 .../apache/geode/session/tests/Tomcat7Test.java |   2 +-
 .../session/tests/Tomcat8ClientServerTest.java  |   4 +-
 .../apache/geode/session/tests/Tomcat8Test.java |   2 +-
 .../session/tests/TomcatClientServerTest.java   |  17 +-
 .../geode/session/tests/TomcatContainer.java    | 154 +++++++
 .../geode/session/tests/TomcatInstall.java      | 342 ++++++---------
 17 files changed, 1293 insertions(+), 788 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java
index 9e5e5c4..f54141b 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java
@@ -45,6 +45,13 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
 
   public abstract ContainerInstall getInstall();
 
+  /**
+   * Sets up the {@link #client} and {@link #manager} variables by creating new instances of each.
+   *
+   * Adds two new containers to the {@link #manager} based on the super class' {@link #getInstall()}
+   * method. Also sets {@link ContainerManager#testName} for {@link #manager} to the name of the
+   * current test.
+   */
   @Before
   public void setup() throws IOException {
     client = new Client();
@@ -54,6 +61,9 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
     manager.addContainers(2, getInstall());
   }
 
+  /**
+   * Stops all containers that were previously started and cleans up their configurations
+   */
   @After
   public void stop() throws IOException {
     manager.stopAllActiveContainers();
@@ -100,7 +110,7 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       resp = client.get(key);
 
       assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
-      assertEquals(value, resp.getResponse());
+      assertEquals("Session data is not replicating properly", value, resp.getResponse());
     }
   }
 
@@ -127,7 +137,7 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       resp = client.get(key);
 
       assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
-      assertEquals(value, resp.getResponse());
+      assertEquals("Container failure caused inaccessible data.", value, resp.getResponse());
     }
   }
 
@@ -152,7 +162,7 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       client.setPort(Integer.parseInt(manager.getContainerPort(i)));
       resp = client.get(key);
 
-      assertEquals("", resp.getResponse());
+      assertEquals("Data removal is not being replicated properly.", "", resp.getResponse());
     }
   }
 
@@ -188,7 +198,8 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       client.setPort(Integer.parseInt(manager.getContainerPort(i)));
       resp = client.get(key);
 
-      assertEquals("", resp.getResponse());
+      assertEquals("Session replication is not doing session expiration correctly.", "",
+          resp.getResponse());
     }
   }
 
@@ -226,7 +237,8 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       resp = client.get(key);
 
       assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
-      assertEquals(value, resp.getResponse());
+      assertEquals("Containers are not replicating session expiration reset", value,
+          resp.getResponse());
     }
   }
 
@@ -266,6 +278,10 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
     }
   }
 
+  /**
+   * Test that a container added to the system after puts still can access the correct sessions and
+   * data.
+   */
   @Test
   public void newContainersShouldShareDataAccess() throws IOException, URISyntaxException {
     manager.startAllInactiveContainers();
@@ -296,7 +312,8 @@ public abstract class CargoTestBase extends JUnit4CacheTestCase {
       resp = client.get(key);
 
       assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
-      assertEquals(value, resp.getResponse());
+      assertEquals("Containers are not properly sharing data with new arrival", value,
+          resp.getResponse());
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/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 65aefa4..d4f76c3 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
@@ -18,7 +18,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.Properties;
@@ -30,14 +29,10 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.logging.log4j.Logger;
-import org.codehaus.cargo.container.configuration.LocalConfiguration;
-import org.codehaus.cargo.container.deployable.WAR;
 import org.codehaus.cargo.container.installer.Installer;
 import org.codehaus.cargo.container.installer.ZipURLInstaller;
-import org.codehaus.cargo.container.property.LoggingLevel;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
@@ -58,116 +53,223 @@ import org.apache.geode.management.internal.configuration.utils.ZipUtils;
 public abstract class ContainerInstall {
   public static final Logger logger = LogService.getLogger();
 
+  private String defaultLocatorAddress;
+  private int defaultLocatorPort;
+
+  private final ConnectionType connType;
+
   private final String INSTALL_PATH;
-  public static final String DEFAULT_INSTALL_DIR = "/tmp/cargo_containers/";
+  private final String MODULE_PATH;
+  private final String WAR_FILE_PATH;
+
   public static final String GEODE_BUILD_HOME = System.getenv("GEODE_HOME");
+  public static final String DEFAULT_INSTALL_DIR = "/tmp/cargo_containers/";
+  public static final String DEFAULT_MODULE_DIR = GEODE_BUILD_HOME + "/tools/Modules/";
+
+  /**
+   * Represents the type of connection used in this installation
+   *
+   * Supports either PEER_TO_PEER or CLIENT_SERVER. Also containers several useful strings needed to
+   * identify XML files or connection types when setting up containers.
+   */
+  public enum ConnectionType {
+    PEER_TO_PEER("peer-to-peer", "cache-peer.xml"),
+    CLIENT_SERVER("client-server", "cache-client.xml");
+
+    private final String name;
+    private final String cacheXMLFileName;
+
+    ConnectionType(String name, String cacheXMLFileName) {
+      this.name = name;
+      this.cacheXMLFileName = cacheXMLFileName;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public String getCacheXMLFileName() {
+      return cacheXMLFileName;
+    }
+  }
 
-  public HashMap<String, String> cacheProperties;
-  public HashMap<String, String> systemProperties;
+  /**
+   * Base class for handling downloading and configuring J2EE installations
+   *
+   * This class contains common logic for downloading and configuring J2EE installations with cargo,
+   * and some common methods for applying geode session replication configuration to those
+   * 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 {
+    this.connType = connType;
 
-  public ContainerInstall(String installDir, String downloadURL) throws MalformedURLException {
     logger.info("Installing container from URL " + downloadURL);
 
     // Optional step to install the container from a URL pointing to its distribution
     Installer installer = new ZipURLInstaller(new URL(downloadURL), "/tmp/downloads", installDir);
     installer.install();
+
+    // Set install home
     INSTALL_PATH = installer.getHome();
-    logger.info("Installed container into " + getInstallPath());
+    // Find and extract the module path
+    MODULE_PATH = findAndExtractModule(moduleName);
+    // Find the session testing war path
+    WAR_FILE_PATH = findSessionTestingWar();
+
+    // Default locator
+    defaultLocatorPort = 8080;
+    defaultLocatorAddress = "localhost";
+
+    logger.info("Installed container into " + getHome());
+  }
+
+  public ServerContainer generateContainer(File containerConfigHome) throws IOException {
+    return generateContainer(containerConfigHome, "");
+  }
 
-    cacheProperties = new HashMap<>();
-    systemProperties = new HashMap<>();
+  public ServerContainer generateContainer(String containerDescriptors) throws IOException {
+    return generateContainer(null, containerDescriptors);
   }
 
   /**
-   * The directory in which this container is installed.
+   * Sets the default locator address and port
    */
-  public String getInstallPath() {
-    return INSTALL_PATH;
+  public void setDefaultLocator(String address, int port) {
+    defaultLocatorAddress = address;
+    defaultLocatorPort = port;
   }
 
   /**
-   * Called by the installation before container startup
+   * Whether the installation is client server
    *
-   * This is mainly used to write properties to whatever format they need to be in for a given
-   * container before the container is started. The reason for doing this is to make sure that
-   * expensive property updates (such as writing to file or building files from the command line)
-   * only happen as often as they are needed. These kinds of updates usually only need to happen on
-   * container startup or addition.
+   * Since an installation can only be client server or peer to peer there is no need for a function
+   * which checks for a peer to peer installation (just check if not client server).
    */
-  public abstract void writeProperties() throws Exception;
+  public boolean isClientServer() {
+    return connType == ConnectionType.CLIENT_SERVER;
+  }
 
   /**
-   * Cargo's specific string to identify the container
+   * Where the installation is located
    */
-  public abstract String getContainerId();
+  public String getHome() {
+    return INSTALL_PATH;
+  }
 
   /**
-   * A human readable description of the container
+   * Where the module is located
+   *
+   * The module contains jars needed for geode session setup as well as default templates for some
+   * needed XML files.
    */
-  public abstract String getContainerDescription();
+  public String getModulePath() {
+    return MODULE_PATH;
+  }
 
   /**
-   * Configure the geode session replication install in this container to connect to the given
-   * locator.
+   * The path to the session testing WAR file
    */
-  public abstract void setLocator(String address, int port) throws Exception;
+  public String getWarFilePath() {
+    return WAR_FILE_PATH;
+  }
 
   /**
-   * Sets the XML file which contains cache properties.
-   *
-   * Normally this XML file would be set to the cache-client.xml or cache-peer.xml files located in
-   * the module's conf directory (located in build/install/apache-geode/tools/Modules/... for
-   * geode-assembly). However, this allows containers to have different XML files so that locators
-   * will not accidentally overwrite each other's when tests are run concurrently.
-   *
-   * The originalXMLFilePath is used to copy the original XML file to the newXMLFilePath so that all
-   * settings previously there are saved and copied over.
+   * @return The enum {@link #connType} which represents the type of connection for this
+   *         installation
    */
-  public void setCacheXMLFile(String originalXMLFilePath, String newXMLFilePath)
-      throws IOException {
-    File moduleXMLFile = new File(originalXMLFilePath);
-    File installXMLFile = new File(newXMLFilePath);
-
-    installXMLFile.getParentFile().mkdirs();
-    FileUtils.copyFile(moduleXMLFile, installXMLFile);
-
-    setSystemProperty("cache-xml-file", installXMLFile.getAbsolutePath());
+  public ConnectionType getConnectionType() {
+    return connType;
   }
 
   /**
-   * Set a geode session replication property. For example enableLocalCache.
+   * Gets the {@link #defaultLocatorAddress}
+   *
+   * This is the address that a container uses by default. Containers themselves can have their own
+   * personal locator address, but will default to this address unless specifically set.
    */
-  public String setCacheProperty(String name, String value) throws IOException {
-    return cacheProperties.put(name, value);
+  public String getDefaultLocatorAddress() {
+    return defaultLocatorAddress;
   }
 
   /**
-   * Set geode distributed system property.
+   * Gets the {@link #defaultLocatorPort}
+   *
+   * This is the port that a container uses by default. Containers themselves can have their own
+   * personal locator port, but will default to this port unless specifically set.
    */
-  public String setSystemProperty(String name, String value) throws IOException {
-    return systemProperties.put(name, value);
+  public int getDefaultLocatorPort() {
+    return defaultLocatorPort;
   }
 
   /**
-   * Get the specified cache property for an install
+   * Gets the cache XML file to use by default for this installation
    */
-  public String getCacheProperty(String name) {
-    return cacheProperties.get(name);
+  public File getCacheXMLFile() {
+    return new File(MODULE_PATH + "/conf/" + getConnectionType().getCacheXMLFileName());
   }
 
   /**
-   * Get the specified system property for an install
+   * Get the server life cycle class that should be used
+   *
+   * Generates the class based on whether the installation's connection type (@link #connType) is
+   * client server or peer to peer.
    */
-  public String getSystemProperty(String name) {
-    return systemProperties.get(name);
+  public String getServerLifeCycleListenerClass() {
+    String className = "org.apache.geode.modules.session.catalina.";
+    switch (connType) {
+      case PEER_TO_PEER:
+        className += "PeerToPeer";
+        break;
+      case CLIENT_SERVER:
+        className += "ClientServer";
+        break;
+      default:
+        throw new IllegalArgumentException(
+            "Bad connection type. Must be either PEER_TO_PEER or CLIENT_SERVER");
+    }
+
+    className += "CacheLifecycleListener";
+    return className;
   }
 
   /**
-   * Callback to allow this install to update the configuration before it is launched
+   * Cargo specific string to identify the container with
+   */
+  public abstract String getInstallId();
+
+  /**
+   * A human readable description of the installation
+   */
+  public abstract String getInstallDescription();
+
+  /**
+   * Get the session manager class to use
+   */
+  public abstract String getContextSessionManagerClass();
+
+  /**
+   * Generates a {@link ServerContainer} from the given {@link ContainerInstall}
+   *
+   * @param containerDescriptors Additional descriptors used to identify a container
    */
-  public void modifyConfiguration(LocalConfiguration configuration) {}
+  public abstract ServerContainer generateContainer(File containerConfigHome,
+      String containerDescriptors) throws IOException;
 
-  protected String findSessionTestingWar() {
+  /**
+   * Get the path to the session testing war by walking up directories to the correct folder.
+   *
+   * NOTE::This walks into the extensions folder and then uses a hardcoded path from there making it
+   * very unreliable if things are moved.
+   */
+  protected static String findSessionTestingWar() {
     // Start out searching directory above current
     String curPath = "../";
 
@@ -196,20 +298,16 @@ public abstract class ContainerInstall {
   }
 
   /**
-   * Return the session testing war file to use for this container.
+   * Finds and extracts the geode module associated with the specified module.
    *
-   * This should be the war generated by the extensions/session-testing-war. For
-   * {@link GenericAppServerInstall} this war is modified to include the geode session replication
-   * components.
+   * @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
    */
-  public WAR getDeployableWAR() {
-    return new WAR(findSessionTestingWar());
-  }
-
-  protected static String findAndExtractModule(String geodeBuildHome, String moduleName)
-      throws IOException {
+  protected static String findAndExtractModule(String moduleName) throws IOException {
     String modulePath = null;
-    String modulesDir = geodeBuildHome + "/tools/Modules/";
+    String modulesDir = DEFAULT_MODULE_DIR;
 
     boolean archive = false;
     logger.info("Trying to access build dir " + modulesDir);
@@ -253,7 +351,7 @@ public abstract class ContainerInstall {
    *        property value the current value. If false, replaces the current property value with the
    *        given property value
    */
-  public void editPropertyFile(String filePath, String propertyName, String propertyValue,
+  protected static void editPropertyFile(String filePath, String propertyName, String propertyValue,
       boolean append) throws Exception {
     FileInputStream input = new FileInputStream(filePath);
     Properties properties = new Properties();
@@ -271,24 +369,29 @@ public abstract class ContainerInstall {
     logger.info("Modified container Property file " + filePath);
   }
 
-  protected void editXMLFile(String XMLPath, String tagId, String tagName, String parentTagName,
-      HashMap<String, String> attributes) {
+  protected static void editXMLFile(String XMLPath, String tagId, String tagName,
+      String parentTagName, HashMap<String, String> attributes) {
     editXMLFile(XMLPath, tagId, tagName, parentTagName, attributes, false);
   }
 
-  protected void editXMLFile(String XMLPath, String tagName, String parentTagName,
+  protected static void editXMLFile(String XMLPath, String tagName, String parentTagName,
       HashMap<String, String> attributes) {
     editXMLFile(XMLPath, null, tagName, parentTagName, attributes, false);
   }
 
-  protected void editXMLFile(String XMLPath, String tagName, String parentTagName,
+  protected static void editXMLFile(String XMLPath, String tagName, String parentTagName,
       HashMap<String, String> attributes, boolean writeOnSimilarAttributeNames) {
     editXMLFile(XMLPath, null, tagName, parentTagName, attributes, writeOnSimilarAttributeNames);
   }
 
   /**
    * Edit the given xml file
-   * 
+   *
+   * Uses {@link #findNodeWithAttribute(Document, String, String, String)},
+   * {@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.
@@ -300,75 +403,24 @@ public abstract class ContainerInstall {
    *        rather than adding a new element. If false, create a new XML element (unless tagId is
    *        not null).
    */
-  protected void editXMLFile(String XMLPath, String tagId, String tagName, String parentTagName,
-      HashMap<String, String> attributes, boolean writeOnSimilarAttributeNames) {
-    // Get XML file to edit
+  protected static void editXMLFile(String XMLPath, String tagId, String tagName,
+      String parentTagName, HashMap<String, String> attributes,
+      boolean writeOnSimilarAttributeNames) {
+
     try {
+      // Get XML file to edit
       DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
       DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
       Document doc = docBuilder.parse(XMLPath);
 
-      boolean hasTag = false;
-      NodeList nodes = doc.getElementsByTagName(tagName);
-
-      // If tags with name were found search to find tag with proper tagId and update its fields
-      if (nodes != null) {
-        for (int i = 0; i < nodes.getLength(); i++) {
-          Node node = nodes.item(i);
-          if (tagId != null) {
-            Node idAttr = node.getAttributes().getNamedItem("id");
-            // Check node for id attribute
-            if (idAttr != null && idAttr.getTextContent().equals(tagId)) {
-              NamedNodeMap nodeAttrs = node.getAttributes();
-
-              // Remove previous attributes
-              while (nodeAttrs.getLength() > 0) {
-                nodeAttrs.removeNamedItem(nodeAttrs.item(0).getNodeName());
-              }
-
-              ((Element) node).setAttribute("id", tagId);
-              // Set to new attributes
-              for (String key : attributes.keySet()) {
-                ((Element) node).setAttribute(key, attributes.get(key));
-                // node.getAttributes().getNamedItem(key).setTextContent(attributes.get(key));
-              }
-
-              hasTag = true;
-              break;
-            }
-          } else if (writeOnSimilarAttributeNames) {
-            NamedNodeMap nodeAttrs = node.getAttributes();
-            boolean updateNode = true;
-
-            // Check to make sure has all attribute fields
-            for (String key : attributes.keySet()) {
-              if (nodeAttrs.getNamedItem(key) == null) {
-                updateNode = false;
-                break;
-              }
-            }
-            // Check to make sure does not have more than attribute fields
-            for (int j = 0; j < nodeAttrs.getLength(); j++) {
-              if (attributes.get(nodeAttrs.item(j).getNodeName()) == null) {
-                updateNode = false;
-                break;
-              }
-            }
-
-            // Update node attributes
-            if (updateNode) {
-              for (String key : attributes.keySet())
-                node.getAttributes().getNamedItem(key).setTextContent(attributes.get(key));
-
-              hasTag = true;
-              break;
-            }
-          }
-
-        }
-      }
-
-      if (!hasTag) {
+      // Get node with specified tagId
+      Node node = findNodeWithAttribute(doc, tagName, "id", tagId);
+      // If no node is found
+      if (node != null
+          || (writeOnSimilarAttributeNames && nodeHasExactAttributes(node, attributes, false))) {
+        rewriteNodeAttributes(node, attributes);
+        ((Element) node).setAttribute("id", tagId);
+      } else {
         Element e = doc.createElement(tagName);
         // Set id attribute
         if (tagId != null)
@@ -395,16 +447,84 @@ public abstract class ContainerInstall {
   }
 
   /**
-   * Get the location of this installations configuration home
+   * 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
+   * @param value The value of the node's given attribute
+   * @return Node with the given name, attribute, and attribute value
    */
-  public String getContainerConfigHome() {
-    return "/tmp/cargo_configs/" + getContainerDescription();
+  private static Node findNodeWithAttribute(Document doc, String nodeName, String name,
+      String value) {
+    NodeList nodes = doc.getElementsByTagName(nodeName);
+    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))
+        return node;
+    }
+
+    return null;
   }
 
   /**
-   * Get the logging level of this install
+   * 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
    */
-  public String getLoggingLevel() {
-    return LoggingLevel.HIGH.getLevel();
+  private static Node rewriteNodeAttributes(Node node, HashMap<String, String> attributes) {
+    NamedNodeMap nodeAttrs = node.getAttributes();
+
+    // Remove all previous attributes
+    while (nodeAttrs.getLength() > 0)
+      nodeAttrs.removeNamedItem(nodeAttrs.item(0).getNodeName());
+
+    // Set to new attributes
+    for (String key : attributes.keySet())
+      ((Element) node).setAttribute(key, attributes.get(key));
+
+    return node;
+  }
+
+  /**
+   * 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
+   *         less attributes). If {@param checkSimilarValues} is true then only returns true if the
+   *         node shares attributes with the given attribute list exactly.
+   */
+  private static boolean nodeHasExactAttributes(Node node, HashMap<String, String> attributes,
+      boolean checkSimilarValues) {
+    NamedNodeMap nodeAttrs = node.getAttributes();
+
+    // Check to make sure the node has all attribute fields
+    for (String key : attributes.keySet()) {
+      Node attr = nodeAttrs.getNamedItem(key);
+      if (attr == null
+          || (checkSimilarValues && !attr.getTextContent().equals(attributes.get(key)))) {
+        return false;
+      }
+    }
+
+    // Check to make sure the node does not have more than the attribute fields
+    for (int i = 0; i < nodeAttrs.getLength(); i++) {
+      String attr = nodeAttrs.item(i).getNodeName();
+      if (attributes.get(attr) == null || (checkSimilarValues
+          && !attributes.get(attr).equals(nodeAttrs.item(i).getTextContent()))) {
+        return false;
+      }
+    }
+
+    return true;
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java
index 2dba38a..500cfa9 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java
@@ -14,26 +14,12 @@
  */
 package org.apache.geode.session.tests;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 
-import org.apache.commons.io.FileUtils;
 import org.apache.logging.log4j.Logger;
-import org.codehaus.cargo.container.ContainerType;
-import org.codehaus.cargo.container.InstalledLocalContainer;
 import org.codehaus.cargo.container.State;
-import org.codehaus.cargo.container.configuration.ConfigurationType;
-import org.codehaus.cargo.container.configuration.LocalConfiguration;
-import org.codehaus.cargo.container.deployable.WAR;
-import org.codehaus.cargo.container.property.GeneralPropertySet;
-import org.codehaus.cargo.container.property.ServletPropertySet;
-import org.codehaus.cargo.container.tomcat.TomcatPropertySet;
-import org.codehaus.cargo.generic.DefaultContainerFactory;
-import org.codehaus.cargo.generic.configuration.DefaultConfigurationFactory;
 
-import org.apache.geode.internal.AvailablePortHelper;
 import org.apache.geode.internal.logging.LogService;
 
 /**
@@ -44,24 +30,24 @@ import org.apache.geode.internal.logging.LogService;
  */
 public class ContainerManager {
   private static final Logger logger = LogService.getLogger();
-
-  private ArrayList<InstalledLocalContainer> containers;
-  private ArrayList<ContainerInstall> installs;
-
   private String testName;
 
+  private ArrayList<ServerContainer> containers;
+
   public ContainerManager() {
     containers = new ArrayList<>();
-    installs = new ArrayList<>();
   }
 
   /**
-   * Set the name of the current test
-   *
-   * Used for debugging so that log files can be easily identified
+   * @return the number of containers managed
    */
-  public void setTestName(String testName) {
-    this.testName = testName;
+  public int numContainers() {
+    return containers.size();
+  }
+
+  public void cleanUp() throws IOException {
+    for (int i = 0; i < numContainers(); i++)
+      getContainer(i).cleanUp();
   }
 
   /**
@@ -69,7 +55,7 @@ public class ContainerManager {
    *
    * The container will not be running until one of the start methods is called.
    */
-  public InstalledLocalContainer addContainer(ContainerInstall install) throws IOException {
+  public ServerContainer addContainer(ContainerInstall install) throws IOException {
     return addContainer(install, containers.size());
   }
 
@@ -84,31 +70,73 @@ public class ContainerManager {
   }
 
   /**
-   * Return the http port the given container is listening on, if the container is running
-   * 
-   * @throws IllegalStateException if the container is not running.
+   * Start the given container
    */
-  public String getContainerPort(int index) {
-    return getContainerPort(getContainer(index));
+  public void startContainer(int index) {
+    try {
+      getContainer(index).start();
+    } catch (Exception e) {
+      throw new RuntimeException("Something very bad happened when trying to start container "
+          + getContainerDescription(index), e);
+    }
+
+    logger.info("Started container " + getContainerDescription(index));
   }
 
-  private String getContainerPort(InstalledLocalContainer container) {
-    LocalConfiguration config = container.getConfiguration();
-    config.applyPortOffset();
+  /**
+   * Start all containers specified by the given indexes
+   */
+  public void startContainers(ArrayList<Integer> indexes) {
+    for (int index : indexes)
+      startContainer(index);
+  }
 
-    if (!container.getState().isStarted()) {
-      throw new IllegalStateException("Port has not yet been assigned to container");
-    }
-    return config.getPropertyValue(ServletPropertySet.PORT);
+  /**
+   * Start all containers that are not currently running.
+   */
+  public void startAllInactiveContainers() {
+    startContainers(getInactiveContainerIndexes());
   }
 
   /**
-   * @return the number of containers managed
+   * Stop the given container
    */
-  public int numContainers() {
-    return containers.size();
+  public void stopContainer(int index) {
+    getContainer(index).stop();
+
+    logger.info("Stopped container " + getContainerDescription(index));
+  }
+
+  /**
+   * Stop all containers specified by the given indexes
+   */
+  public void stopContainers(ArrayList<Integer> indexes) {
+    for (int index : indexes)
+      stopContainer(index);
+  }
+
+  /**
+   * Stop all containers that are currently running.
+   */
+  public void stopAllActiveContainers() {
+    stopContainers(getActiveContainerIndexes());
+  }
+
+  /**
+   * Set the name of the current test
+   *
+   * Used for debugging so that log files can be easily identified.
+   */
+  public void setTestName(String testName) {
+    this.testName = testName;
   }
 
+  /**
+   * Get the positions of the containers with the given container state
+   *
+   * @param state A string representing the Cargo state a container is in. The possible states can
+   *        be found in as static variables in the {@link State} class.
+   */
   public ArrayList<Integer> getContainerIndexesWithState(String state) {
     ArrayList<Integer> indexes = new ArrayList<>();
     for (int i = 0; i < numContainers(); i++) {
@@ -119,7 +147,7 @@ public class ContainerManager {
           indexes.add(i);
       } else
         throw new IllegalArgumentException(
-            "State must be one of the 5 specified cargo state strings (stopped, started, starting, stopping, or unknown). Given: "
+            "State must be one of the 5 specified cargo state strings (stopped, started, starting, stopping, or unknown). State given was: "
                 + state);
     }
     return indexes;
@@ -128,231 +156,77 @@ public class ContainerManager {
   /**
    * Return the cargo container of all of the containers in the given state
    */
-  public ArrayList<InstalledLocalContainer> getContainersWithState(String state) {
-    ArrayList<InstalledLocalContainer> statedContainers = new ArrayList<>();
+  public ArrayList<ServerContainer> getContainersWithState(String state) {
+    ArrayList<ServerContainer> statedContainers = new ArrayList<>();
     for (int index : getContainerIndexesWithState(state))
       statedContainers.add(getContainer(index));
     return statedContainers;
   }
 
-  private ArrayList<Integer> getInactiveContainerIndexes() {
-    ArrayList<Integer> indexes = getContainerIndexesWithState(State.STOPPED.toString());
-    indexes.addAll(getContainerIndexesWithState(State.UNKNOWN.toString()));
-    return indexes;
-  }
-
-  private ArrayList<InstalledLocalContainer> getInactiveContainers() {
-    ArrayList<InstalledLocalContainer> inactiveContainers =
-        getContainersWithState(State.STOPPED.toString());
-    inactiveContainers.addAll(getContainersWithState(State.UNKNOWN.toString()));
-    return inactiveContainers;
-  }
-
-  private ArrayList<Integer> getActiveContainerIndexes() {
-    return getContainerIndexesWithState(State.STARTED.toString());
-  }
-
-  public ArrayList<InstalledLocalContainer> getActiveContainers() {
-    return getContainersWithState(State.STARTED.toString());
-  }
-
-  public InstalledLocalContainer getContainer(int index) {
-    return containers.get(index);
-  }
-
-  public ContainerInstall getContainerInstall(int index) {
-    return installs.get(index);
-  }
-
   /**
-   * Get a textual description of the given container.
+   * Get the port of the container at the given index
    */
-  public String getContainerDescription(int index) {
-    return getContainerDescription(getContainer(index), getContainerInstall(index)) + " (" + index
-        + ")";
-  }
-
-  private String getContainerDescription(InstalledLocalContainer container,
-      ContainerInstall install) {
-    String port = "<" + container.getState().toString() + ">";
-    try {
-      port = String.valueOf(getContainerPort(container));
-    } catch (IllegalStateException ise) {
-    }
-
-    return install.getContainerDescription() + ":" + port;
+  public String getContainerPort(int index) {
+    return getContainer(index).getPort();
   }
 
   /**
-   * Start the given container
+   * Get the container at the given index
    */
-  public void startContainer(int index) {
-    InstalledLocalContainer container = getContainer(index);
-    ContainerInstall install = getContainerInstall(index);
-    String containerDescription = getContainerDescription(index);
-
-    String logFilePath =
-        new File("cargo_logs/containers/" + getUniqueContainerDescription(index) + ".log")
-            .getAbsolutePath();
-    container.setOutput(logFilePath);
-    logger.info("Sending log file output to " + logFilePath);
-
-    if (!container.getState().isStarted()) {
-      logger.info("Starting container " + containerDescription);
-      int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(3);
-      container.getConfiguration().setProperty(ServletPropertySet.PORT, Integer.toString(ports[0]));
-      container.getConfiguration().setProperty(GeneralPropertySet.RMI_PORT,
-          Integer.toString(ports[1]));
-      container.getConfiguration().setProperty(TomcatPropertySet.AJP_PORT,
-          Integer.toString(ports[2]));
-      container.getConfiguration().setProperty(GeneralPropertySet.PORT_OFFSET, "0");
-
-      try {
-        install.writeProperties();
-        container.start();
-      } catch (Exception e) {
-        throw new RuntimeException(
-            "Something very bad happened to this container when starting. Check the cargo_logs folder for container logs.",
-            e);
-      }
-      logger.info("Started container " + containerDescription);
-    } else {
-      throw new IllegalArgumentException("Cannot start container " + containerDescription
-          + " its current state is " + container.getState());
-    }
+  public ServerContainer getContainer(int index) {
+    return containers.get(index);
   }
 
   /**
-   * Stop the given container
+   * Get a human readable unique description for the container (calls
+   * {@link ServerContainer#toString()})
    */
-  public void stopContainer(int index) {
-    InstalledLocalContainer container = getContainer(index);
-    if (container.getState().isStarted()) {
-      logger.info("Stopping container" + index + " " + getContainerDescription(index));
-      container.stop();
-      logger.info("Stopped container" + index + " " + getContainerDescription(index));
-    } else
-      throw new IllegalArgumentException("Cannot stop container " + getContainerDescription(index)
-          + " it is currently " + container.getState());
-  }
-
-  public void startContainers(ArrayList<Integer> indexes) {
-    for (int index : indexes)
-      startContainer(index);
-  }
-
-  public void stopContainers(ArrayList<Integer> indexes) {
-    for (int index : indexes)
-      stopContainer(index);
+  public String getContainerDescription(int index) {
+    return getContainer(index).toString();
   }
 
   /**
-   * Start all containers that are not currently running.
+   * Create a container to manage, given an installation.
    */
-  public void startAllInactiveContainers() {
-    startContainers(getInactiveContainerIndexes());
-  }
+  private ServerContainer addContainer(ContainerInstall install, int index) throws IOException {
+    ServerContainer container = install.generateContainer(testName + "_" + index);
 
-  /**
-   * Stop all containers that are currently running.
-   */
-  public void stopAllActiveContainers() {
-    stopContainers(getActiveContainerIndexes());
-  }
+    containers.add(index, container);
 
-  public void removeContainer(int index) {
-    stopContainer(index);
-    containers.remove(index);
-    installs.remove(index);
+    logger.info("Setup container " + getContainerDescription(index));
+    return container;
   }
 
   /**
-   * Runs {@link #clean} on all containers
+   * Get the indexes of all active containers
    */
-  public void cleanUp() throws IOException {
-    for (int i = 0; i < numContainers(); i++)
-      clean(i);
+  private ArrayList<Integer> getActiveContainerIndexes() {
+    return getContainerIndexesWithState(State.STARTED.toString());
   }
 
   /**
-   * Deletes the configuration directory for the specified container
+   * Get all active containers
    */
-  private void clean(int index) throws IOException {
-    ContainerInstall install = getContainerInstall(index);
-
-    String baseLogFilePath = new File("cargo_logs").getAbsolutePath();
-    String configLogFolderPath = baseLogFilePath + "/configs/";
-
-    File configDir = new File(getContainer(index).getConfiguration().getHome());
-    File configLogDir = new File(configLogFolderPath + configDir.getName());
-
-    if (configDir.exists()) {
-      configLogDir.mkdirs();
-
-      logger.info("Configuration in " + configDir.getAbsolutePath());
-      FileUtils.copyDirectory(configDir, configLogDir);
-      logger.info("Copied configuration to " + configLogDir.getAbsolutePath());
-      logger.info("Deleting configuration folder " + configDir.getAbsolutePath());
-      FileUtils.deleteDirectory(configDir);
-    }
-  }
-
-  private String getUniqueContainerDescription(int index) {
-    return getUniqueContainerDescription(index, getContainerInstall(index));
+  private ArrayList<ServerContainer> getActiveContainers() {
+    return getContainersWithState(State.STARTED.toString());
   }
 
   /**
-   * Get a human readable unique container description for container storage.
-   *
-   * Unique descriptions currently are generated by joining
-   * {@link ContainerInstall#getContainerDescription()}, the index passed in, {@link #testName}, and
-   * the {@link System#nanoTime()} with '_' characters
+   * Get the indexes of all inactive containers
    */
-  private String getUniqueContainerDescription(int index, ContainerInstall install) {
-    return String.join("_", Arrays.asList(install.getContainerDescription(),
-        Integer.toString(index), testName, Long.toString(System.nanoTime())));
+  private ArrayList<Integer> getInactiveContainerIndexes() {
+    ArrayList<Integer> indexes = getContainerIndexesWithState(State.STOPPED.toString());
+    indexes.addAll(getContainerIndexesWithState(State.UNKNOWN.toString()));
+    return indexes;
   }
 
   /**
-   * Create a container to manage, given an installation.
+   * Get all inactive containers
    */
-  private InstalledLocalContainer addContainer(ContainerInstall install, int index)
-      throws IOException {
-    String uniqueName = getUniqueContainerDescription(index, install);
-
-    // Create the Cargo Container instance wrapping our physical container
-    LocalConfiguration configuration = (LocalConfiguration) new DefaultConfigurationFactory()
-        .createConfiguration(install.getContainerId(), ContainerType.INSTALLED,
-            ConfigurationType.STANDALONE, "/tmp/cargo_configs/" + uniqueName);
-    configuration.setProperty(GeneralPropertySet.LOGGING, install.getLoggingLevel());
-
-    install.modifyConfiguration(configuration);
-
-    File gemfireLogFile = new File("cargo_logs/gemfire_modules/" + uniqueName);
-    gemfireLogFile.getParentFile().mkdirs();
-    install.setSystemProperty("log-file", gemfireLogFile.getAbsolutePath());
-    logger.info("Gemfire logs in " + gemfireLogFile.getAbsolutePath());
-
-    // Removes secureRandom generation so that container startup is much faster
-    configuration.setProperty(GeneralPropertySet.JVMARGS,
-        "-Djava.security.egd=file:/dev/./urandom");
-
-    // Statically deploy WAR file for servlet
-    WAR war = install.getDeployableWAR();
-    war.setContext("");
-    configuration.addDeployable(war);
-    logger.info("Deployed WAR file at " + war.getFile());
-
-    // Create the container, set it's home dir to where it was installed, and set the its output log
-    InstalledLocalContainer container = (InstalledLocalContainer) (new DefaultContainerFactory())
-        .createContainer(install.getContainerId(), ContainerType.INSTALLED, configuration);
-
-    container.setHome(install.getInstallPath());
-
-    containers.add(index, container);
-    installs.add(index, install);
-
-    logger.info("Setup container " + getContainerDescription(index));
-    return container;
+  private ArrayList<ServerContainer> getInactiveContainers() {
+    ArrayList<ServerContainer> inactiveContainers =
+        getContainersWithState(State.STOPPED.toString());
+    inactiveContainers.addAll(getContainersWithState(State.UNKNOWN.toString()));
+    return inactiveContainers;
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerContainer.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerContainer.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerContainer.java
new file mode 100644
index 0000000..0694e6f
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerContainer.java
@@ -0,0 +1,150 @@
+/*
+ * 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.apache.geode.session.tests.ContainerInstall.GEODE_BUILD_HOME;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assume;
+
+/**
+ * Container for a generic app server
+ *
+ * Extends {@link ServerContainer} to form a basic container which sets up a GenericAppServer
+ * container. Currently being used solely for Jetty 9 containers.
+ *
+ * The container modifies a copy of the session testing war using the modify_war_file script in
+ * order to properly implement geode session replication for generic application servers. That means
+ * that tests using this container will only run on linux.
+ *
+ * In theory, adding support for additional containers should just be a matter of changing the
+ * {@link GenericAppServerInstall} to support other installations, since this container does not
+ * depend upon the type of appserver in any way.
+ */
+public class GenericAppServerContainer extends ServerContainer {
+  private final File modifyWarScript;
+
+  private static final String DEFAULT_GENERIC_APPSERVER_WAR_DIR = "/tmp/cargo_wars/";
+
+  /**
+   * Setup the generic appserver container
+   *
+   * Sets up a configuration for the container using the specified installation and configuration
+   * home. Finds the script needed to modify the war file, sets up the new WAR file to modify by
+   * creating a temporary WAR file to use, deploys the war to the Cargo container, and sets various
+   * container properties (i.e. locator, local cache, etc.)
+   */
+  public GenericAppServerContainer(GenericAppServerInstall install, File containerConfigHome,
+      String containerDescriptors) throws IOException {
+    super(install, containerConfigHome, containerDescriptors);
+
+    // Setup modify war script file so that it is executable and easily findable
+    modifyWarScript = new File(install.getModulePath() + "/bin/modify_war");
+    modifyWarScript.setExecutable(true);
+
+    // Ignore tests that are running on windows, since they can't run the modify war script
+    Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"));
+
+    // Create temp war file to use
+    File warDir = new File(DEFAULT_GENERIC_APPSERVER_WAR_DIR);
+    warDir.mkdirs();
+    setWarFile(File.createTempFile(description, ".war", warDir));
+
+    // Deploy war file to container configuration
+    deployWar();
+    // Setup the default installations locators
+    setLocator(install.getDefaultLocatorAddress(), install.getDefaultLocatorPort());
+
+    // Make sure that local caches are disabled by default
+    setCacheProperty("enable_local_cache", "false");
+  }
+
+  /**
+   * Builds the command needed to run the {@link #modifyWarScript}
+   *
+   * The command is built as an array list of strings with each element representing a string
+   * separated by a space on the command line. For example, the list {'-t', 'geode'} would represent
+   * 'modify_war -t geode' on the command line.
+   *
+   * The command built points towards the {@link ContainerInstall#getWarFilePath()} as the starting
+   * file and the {@link #warFile} as the output file. Cache and system properties are specified
+   * when modifying the WAR, so the elements contained within the {@link #cacheProperties} and
+   * {@link #systemProperties} maps are also added to the command built.
+   */
+  private List<String> buildCommand() throws IOException {
+    ContainerInstall install = getInstall();
+
+    List<String> command = new ArrayList<>();
+    command.add(modifyWarScript.getAbsolutePath());
+    command.add("-w");
+    command.add(install.getWarFilePath());
+    command.add("-t");
+    command.add(install.getConnectionType().getName());
+    command.add("-o");
+    command.add(getWarFile().getAbsolutePath());
+    for (String property : cacheProperties.keySet()) {
+      command.add("-p");
+      command.add("gemfire.cache." + property + "=" + getCacheProperty(property));
+    }
+    for (String property : systemProperties.keySet()) {
+      command.add("-p");
+      command.add("gemfire.property." + property + "=" + getSystemProperty(property));
+    }
+
+    return command;
+  }
+
+  /**
+   * Modifies the {@link ContainerInstall#getWarFilePath()} for container use, by simulating a
+   * command line execution of the modify_war_file script using the commands built from
+   * {@link #buildCommand()}
+   *
+   * The modified WAR file is sent to {@link #warFile}.
+   * 
+   * @throws IOException If the command executed returns with a non-zero exit code.
+   */
+  private void modifyWarFile() throws IOException, InterruptedException {
+    // Build the environment to run the command
+    ProcessBuilder builder = new ProcessBuilder();
+    builder.environment().put("GEODE", GEODE_BUILD_HOME);
+    builder.inheritIO();
+    // Setup the environment builder with the command
+    builder.command(buildCommand());
+    logger.info("Running command: " + String.join(" ", builder.command()));
+
+    // Run the command
+    Process process = builder.start();
+
+    // Wait for the command to finish
+    int exitCode = process.waitFor();
+    // Throw error if bad exit
+    if (exitCode != 0) {
+      throw new IOException("Unable to run modify_war script: " + builder.command());
+    }
+  }
+
+  /**
+   * Update the container's settings by calling by modifying the war file through the
+   * {@link #modifyWarFile()} function
+   */
+  @Override
+  public void writeSettings() throws Exception {
+    modifyWarFile();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java
index f5072bb..aead718 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java
@@ -14,47 +14,48 @@
  */
 package org.apache.geode.session.tests;
 
+import java.awt.Container;
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.codehaus.cargo.container.deployable.WAR;
-import org.junit.Assume;
 
 /**
  * Container install for a generic app server
  *
- * Extends {@link ContainerInstall} to form a basic installer which downloads and sets up a
- * container installation. Currently being used solely for Jetty 9 installation.
+ * Extends {@link ContainerInstall} to form a basic installer which downloads and sets up an
+ * installation to build a container off of. Currently being used solely for Jetty 9 installation.
  *
- * This install modifies the session testing war using the modify_war_file script, so that it uses
- * the geode session replication for generic application servers. That also means that tests using
- * this install will only run on linux.
+ * This install is used to setup many different generic app server containers using
+ * {@link GenericAppServerContainer}.
  *
- * In theory, adding support for additional containers should just be a matter of adding new
- * elements to the {@link Server} enumeration, since this install does not modify the container in
- * any way.
+ * In theory, adding support for additional appserver installations should just be a matter of
+ * adding new elements to the {@link GenericAppServerVersion} enumeration, since this install does
+ * not do much modification of the installation itself. There is very little (maybe no) Jetty 9
+ * specific code outside of the {@link GenericAppServerVersion}.
  */
 public class GenericAppServerInstall extends ContainerInstall {
 
   /**
-   * Get the download URL of a generic app server using hardcoded keywords
+   * Get the download URL and container name of a generic app server using hardcoded keywords
    *
-   * Currently the only supported keyword instance is JETTY9
+   * Currently the only supported keyword instance is JETTY9.
    */
-  public enum Server {
-    JETTY9(
+  public enum GenericAppServerVersion {
+    JETTY9(9,
         "http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.5.v20170502/jetty-distribution-9.4.5.v20170502.zip",
-        "jetty9x");
+        "jetty");
 
-    private String downloadURL;
-    private String containerId;
+    private final int version;
+    private final String downloadURL;
+    private final String containerName;
 
-    Server(String downloadURL, String containerId) {
+    GenericAppServerVersion(int version, String downloadURL, String containerName) {
+      this.version = version;
       this.downloadURL = downloadURL;
-      this.containerId = containerId;
+      this.containerName = containerName;
+    }
+
+    public int getVersion() {
+      return version;
     }
 
     public String getDownloadURL() {
@@ -62,186 +63,79 @@ public class GenericAppServerInstall extends ContainerInstall {
     }
 
     public String getContainerId() {
-      return containerId;
-    }
-  }
-
-  /**
-   * Represent the type of cache being used in this install
-   *
-   * Supports PEER_TO_PEER or CLIENT_SERVER. Also contains several useful helper functions
-   * containing hardcoded values needed for the two different types of caches.
-   */
-  public enum CacheType {
-    PEER_TO_PEER("peer-to-peer", "cache-peer.xml"),
-    CLIENT_SERVER("client-server", "cache-client.xml");
-
-    private final String commandLineTypeString;
-    private final String XMLTypeFile;
-
-    CacheType(String commandLineTypeString, String XMLTypeFile) {
-      this.commandLineTypeString = commandLineTypeString;
-      this.XMLTypeFile = XMLTypeFile;
-    }
-
-    public String getCommandLineTypeString() {
-      return commandLineTypeString;
-    }
-
-    public String getXMLTypeFile() {
-      return XMLTypeFile;
+      return containerName + getVersion() + "x";
     }
   }
 
-  private File warFile;
-  private CacheType cacheType;
-  private Server server;
-
-  private final String appServerModulePath;
+  private GenericAppServerVersion version;
 
-  public GenericAppServerInstall(Server server) throws IOException, InterruptedException {
-    this(server, CacheType.PEER_TO_PEER, DEFAULT_INSTALL_DIR);
-  }
-
-  public GenericAppServerInstall(Server server, String installDir)
+  public GenericAppServerInstall(GenericAppServerVersion version)
       throws IOException, InterruptedException {
-    this(server, CacheType.PEER_TO_PEER, installDir);
+    this(version, ConnectionType.PEER_TO_PEER, DEFAULT_INSTALL_DIR);
   }
 
-  public GenericAppServerInstall(Server server, CacheType cacheType)
+  public GenericAppServerInstall(GenericAppServerVersion version, String installDir)
       throws IOException, InterruptedException {
-    this(server, cacheType, DEFAULT_INSTALL_DIR);
+    this(version, ConnectionType.PEER_TO_PEER, installDir);
   }
 
-  /**
-   * Download and setup container installation
-   *
-   * Finds the path to (and extracts) the appserver module located within GEODE_BUILD_HOME
-   * directory. If cache is Client Server then also builds WAR file.
-   */
-  public GenericAppServerInstall(Server server, CacheType cacheType, String installDir)
+  public GenericAppServerInstall(GenericAppServerVersion version, ConnectionType cacheType)
       throws IOException, InterruptedException {
-    super(installDir, server.getDownloadURL());
-
-    // Ignore tests that are running on windows, since they can't run the modify war script
-    Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"));
-    this.server = server;
-    this.cacheType = cacheType;
-
-    appServerModulePath = findAndExtractModule(GEODE_BUILD_HOME, "appserver");
-    // Set the cache XML file by copying the XML file in the build dir
-    setCacheXMLFile(appServerModulePath + "/conf/" + cacheType.getXMLTypeFile(),
-        "cargo_logs/XMLs/" + getContainerDescription() + ".xml");
-
-    // Default properties
-    setCacheProperty("enable_local_cache", "false");
-
-    warFile = File.createTempFile("session-testing", ".war", new File("/tmp"));
-    warFile.deleteOnExit();
+    this(version, cacheType, DEFAULT_INSTALL_DIR);
   }
 
   /**
-   * Build the command list used to modify/build the WAR file
+   * Download and setup container installation of a generic appserver using the
+   * {@link ContainerInstall} constructor and some hardcoded module values
    */
-  private List<String> buildCommand() throws IOException {
-    String unmodifiedWar = findSessionTestingWar();
-    String modifyWarScript = appServerModulePath + "/bin/modify_war";
-    new File(modifyWarScript).setExecutable(true);
-
-    List<String> command = new ArrayList<>();
-    command.add(modifyWarScript);
-    command.add("-w");
-    command.add(unmodifiedWar);
-    command.add("-t");
-    command.add(cacheType.getCommandLineTypeString());
-    command.add("-o");
-    command.add(warFile.getAbsolutePath());
-    for (String property : cacheProperties.keySet()) {
-      command.add("-p");
-      command.add("gemfire.cache." + property + "=" + getCacheProperty(property));
-    }
-    for (String property : systemProperties.keySet()) {
-      command.add("-p");
-      command.add("gemfire.property." + property + "=" + getSystemProperty(property));
-    }
-
-    return command;
-  }
-
-  /**
-   * Modifies the WAR file for container use, by simulating a command line execution of the
-   * modify_war_file script using the commands built from {@link #buildCommand()}
-   */
-  private void modifyWarFile() throws IOException, InterruptedException {
-    ProcessBuilder builder = new ProcessBuilder();
-    builder.environment().put("GEODE", GEODE_BUILD_HOME);
-    builder.inheritIO();
-
-    builder.command(buildCommand());
-    logger.info("Running command: " + String.join(" ", builder.command()));
+  public GenericAppServerInstall(GenericAppServerVersion version, ConnectionType connType,
+      String installDir) throws IOException, InterruptedException {
+    super(installDir, version.getDownloadURL(), connType, "appserver");
 
-    Process process = builder.start();
-
-    int exitCode = process.waitFor();
-    if (exitCode != 0) {
-      throw new IOException("Unable to run modify_war script: " + builder.command());
-    }
+    this.version = version;
   }
 
   /**
-   * AppServer specific property updater
+   * Implementation of {@link ContainerInstall#generateContainer(File, String)}, which generates a
+   * generic appserver specific container
+   *
+   * Creates a {@link GenericAppServerContainer} instance off of this installation.
    *
-   * Overrides {@link ContainerInstall#writeProperties}. Since most properties for an app server can
-   * be specified through flags when running the modify_war script this runs
-   * {@link #modifyWarFile()}.
+   * @param containerDescriptors Additional descriptors used to identify a container
    */
   @Override
-  public void writeProperties() throws Exception {
-    modifyWarFile();
+  public GenericAppServerContainer generateContainer(File containerConfigHome,
+      String containerDescriptors) throws IOException {
+    return new GenericAppServerContainer(this, containerConfigHome, containerDescriptors);
   }
 
   /**
-   * @see ContainerInstall#getContainerId()
+   * The cargo specific installation id needed to setup a cargo container
+   *
+   * Based on the installation's {@link #version}.
    */
   @Override
-  public String getContainerId() {
-    return server.getContainerId();
+  public String getInstallId() {
+    return version.getContainerId();
   }
 
   /**
-   * @see ContainerInstall#getContainerDescription()
+   * @see ContainerInstall#getInstallDescription()
    */
   @Override
-  public String getContainerDescription() {
-    return server.name() + "_" + cacheType.name();
+  public String getInstallDescription() {
+    return version.name() + "_" + getConnectionType().getName();
   }
 
   /**
-   * Sets the locator for this container
+   * Implements {@link ContainerInstall#getContextSessionManagerClass()}
    *
-   * If the cache is P2P the WAR file must be regenerated to take a new locator. Otherwise (if
-   * Client Server) the cache xml file will be edited.
-   */
-  @Override
-  public void setLocator(String address, int port) throws Exception {
-    if (cacheType == CacheType.PEER_TO_PEER) {
-      setSystemProperty("locators", address + "[" + port + "]");
-    } else {
-      HashMap<String, String> attributes = new HashMap<>();
-      attributes.put("host", address);
-      attributes.put("port", Integer.toString(port));
-
-      editXMLFile(getSystemProperty("cache-xml-file"), "locator", "pool", attributes, true);
-    }
-
-    logger.info("Set locator for AppServer install to " + address + "[" + port + "]");
-  }
-
-  /**
-   * @see ContainerInstall#getDeployableWAR()
+   * @throws IllegalArgumentException Always throws an illegal argument exception because app
+   *         servers should not need the session manager class
    */
   @Override
-  public WAR getDeployableWAR() {
-    return new WAR(warFile.getAbsolutePath());
+  public String getContextSessionManagerClass() {
+    throw new IllegalArgumentException(
+        "Bad method call. Generic app servers do not use TomcatDeltaSessionManagers.");
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java
index 5cd7726..84b81bf 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java
@@ -29,10 +29,10 @@ public class Jetty9ClientServerTest extends GenericAppServerClientServerTest {
 
   @BeforeClass
   public static void setupJettyInstall() throws Exception {
-    install = new GenericAppServerInstall(GenericAppServerInstall.Server.JETTY9,
-        GenericAppServerInstall.CacheType.CLIENT_SERVER,
+    install = new GenericAppServerInstall(GenericAppServerInstall.GenericAppServerVersion.JETTY9,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Jetty9ClientServerTest");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java
index 813362b..ed3d8b1 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java
@@ -29,9 +29,10 @@ public class Jetty9Test extends CargoTestBase {
 
   @BeforeClass
   public static void setupJettyInstall() throws Exception {
-    install = new GenericAppServerInstall(GenericAppServerInstall.Server.JETTY9,
+    install = new GenericAppServerInstall(GenericAppServerInstall.GenericAppServerVersion.JETTY9,
+        ContainerInstall.ConnectionType.PEER_TO_PEER,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Jetty9Test");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/ServerContainer.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/ServerContainer.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/ServerContainer.java
new file mode 100644
index 0000000..ba635ac
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/ServerContainer.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.session.tests;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.logging.log4j.Logger;
+import org.codehaus.cargo.container.ContainerType;
+import org.codehaus.cargo.container.InstalledLocalContainer;
+import org.codehaus.cargo.container.State;
+import org.codehaus.cargo.container.configuration.ConfigurationType;
+import org.codehaus.cargo.container.configuration.LocalConfiguration;
+import org.codehaus.cargo.container.deployable.WAR;
+import org.codehaus.cargo.container.property.GeneralPropertySet;
+import org.codehaus.cargo.container.property.LoggingLevel;
+import org.codehaus.cargo.container.property.ServletPropertySet;
+import org.codehaus.cargo.container.tomcat.TomcatPropertySet;
+import org.codehaus.cargo.generic.DefaultContainerFactory;
+import org.codehaus.cargo.generic.configuration.DefaultConfigurationFactory;
+
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.logging.LogService;
+
+/**
+ * Base class for handling the setup and configuration of cargo containers
+ *
+ * This class contains common logic for setting up and configuring cargo containers for J2EE
+ * container installations. Also includes some common methods for applying geode session replication
+ * configuration to those containers.
+ *
+ * Subclasses provide setup and configuration of specific containers.
+ */
+public abstract class ServerContainer {
+  private InstalledLocalContainer container;
+  private ContainerInstall install;
+
+  private String locatorAddress;
+  private int locatorPort;
+  private File warFile;
+
+  public String description;
+  public File gemfireLogFile;
+  public File cacheXMLFile;
+  public File logDir;
+
+  public String loggingLevel;
+
+  public HashMap<String, String> cacheProperties;
+  public HashMap<String, String> systemProperties;
+
+  public final String DEFAULT_CONF_DIR;
+
+  public static final String DEFAULT_LOGGING_LEVEL = LoggingLevel.HIGH.getLevel();
+  public static final String DEFAULT_LOG_DIR = "cargo_logs/";
+  public static final String DEFAULT_CONFIG_DIR = "/tmp/cargo_configs/";
+
+  public static final Logger logger = LogService.getLogger();
+
+  /**
+   * Sets up the container using the given installation
+   *
+   * Sets up a bunch of logging files, default locations, and container properties.
+   *
+   * Creates a whole new cargo configuration and cargo container for the {@link #container}
+   * variable.
+   *
+   * @param containerConfigHome The folder that the container configuration folder should be setup
+   *        in
+   * @param containerDescriptors A string of extra descriptors for the container used in the
+   *        containers {@link #description}
+   */
+  public ServerContainer(ContainerInstall install, File containerConfigHome,
+      String containerDescriptors) throws IOException {
+    this.install = install;
+    // Get a container description for logging and output
+    description = generateUniqueContainerDescription(containerDescriptors);
+    loggingLevel = DEFAULT_LOGGING_LEVEL;
+    logDir = new File(DEFAULT_LOG_DIR + description);
+
+    logger.info("Creating new container " + description);
+
+    DEFAULT_CONF_DIR = install.getHome() + "/conf/";
+    // Use the default configuration home path if not passed a config home
+    if (containerConfigHome == null)
+      containerConfigHome = new File(DEFAULT_CONFIG_DIR + description);
+
+    // Init the property lists
+    cacheProperties = new HashMap<>();
+    systemProperties = new HashMap<>();
+    // Set WAR file to session testing war
+    warFile = new File(install.getWarFilePath());
+    // Setup logging folders
+    logDir.mkdirs();
+
+    // Create the Cargo Container instance wrapping our physical container
+    LocalConfiguration configuration = (LocalConfiguration) new DefaultConfigurationFactory()
+        .createConfiguration(install.getInstallId(), ContainerType.INSTALLED,
+            ConfigurationType.STANDALONE, containerConfigHome.getAbsolutePath());
+    // Set configuration/container logging level
+    configuration.setProperty(GeneralPropertySet.LOGGING, loggingLevel);
+    // Removes secureRandom generation so that container startup is much faster
+    configuration.setProperty(GeneralPropertySet.JVMARGS,
+        "-Djava.security.egd=file:/dev/./urandom");
+
+    // Setup the gemfire log file for this container
+    gemfireLogFile = new File(logDir.getAbsolutePath() + "/gemfire.log");
+    gemfireLogFile.getParentFile().mkdirs();
+    setSystemProperty("log-file", gemfireLogFile.getAbsolutePath());
+    logger.info("Gemfire logs in " + gemfireLogFile.getAbsolutePath());
+
+    // Create the container
+    container = (InstalledLocalContainer) (new DefaultContainerFactory())
+        .createContainer(install.getInstallId(), ContainerType.INSTALLED, configuration);
+    // Set container's home dir to where it was installed
+    container.setHome(install.getHome());
+    // Set container output log
+    container.setOutput(logDir.getAbsolutePath() + "/container.log");
+
+    // Set cacheXML file
+    File installXMLFile = install.getCacheXMLFile();
+    setCacheXMLFile(new File(logDir.getAbsolutePath() + "/" + installXMLFile.getName()));
+    // Copy the cacheXML file to a new, unique location for this container
+    FileUtils.copyFile(installXMLFile, cacheXMLFile);
+  }
+
+  /**
+   * Generates a unique, mostly human readable, description string of the container using the
+   * installation's description, extraIdentifiers, and the current system nano time
+   */
+  public String generateUniqueContainerDescription(String extraIdentifiers) {
+    return String.join("_", Arrays.asList(install.getInstallDescription(), extraIdentifiers,
+        Long.toString(System.nanoTime())));
+  }
+
+  /**
+   * Deploys the {@link #warFile} to the cargo container ({@link #container}).
+   */
+  public void deployWar() {
+    // Get the cargo war from the war file
+    WAR war = new WAR(warFile.getAbsolutePath());
+    // Set context access to nothing
+    war.setContext("");
+    // Deploy the war the container's configuration
+    getConfiguration().addDeployable(war);
+
+    logger.info("Deployed WAR file at " + war.getFile());
+  }
+
+  /**
+   * Starts this cargo container by picking the container's ports (RMI, AJP, and regular) and
+   * calling the cargo container's start function
+   */
+  public void start() {
+    if (container.getState().isStarted())
+      throw new IllegalArgumentException("Container " + description
+          + " failed to start because it is currently " + container.getState());
+
+    LocalConfiguration config = getConfiguration();
+    int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(3);
+    // Set container ports from available ports
+    config.setProperty(ServletPropertySet.PORT, Integer.toString(ports[0]));
+    config.setProperty(GeneralPropertySet.RMI_PORT, Integer.toString(ports[1]));
+    config.setProperty(TomcatPropertySet.AJP_PORT, Integer.toString(ports[2]));
+    config.setProperty(GeneralPropertySet.PORT_OFFSET, "0");
+    container.setConfiguration(config);
+
+    try {
+      logger.info("Starting container " + description);
+      writeSettings();
+      container.start();
+    } catch (Exception e) {
+      throw new RuntimeException(
+          "Something very bad happened to this container when starting. Check the cargo_logs folder for container logs.",
+          e);
+    }
+  }
+
+  /**
+   * Stops this cargo container
+   */
+  public void stop() {
+    if (!container.getState().isStarted()) {
+      throw new IllegalArgumentException("Container " + description
+          + " failed to stop because it is currently " + container.getState());
+    }
+
+    container.stop();
+  }
+
+  /**
+   * Copies the container configuration (found through {@link #getConfiguration()}) to the logging
+   * directory specified by {@link #logDir}
+   */
+  public void cleanUp() throws IOException {
+    File configDir = new File(getConfiguration().getHome());
+
+    if (configDir.exists()) {
+      logger.info("Configuration in " + configDir.getAbsolutePath());
+      FileUtils.copyDirectory(configDir, new File(logDir.getAbsolutePath() + "/config"));
+      logger.info("Copied configuration to "
+          + new File(logDir.getAbsolutePath() + "/config").getAbsolutePath());
+      logger.info("Deleting configuration folder " + configDir.getAbsolutePath());
+      FileUtils.deleteDirectory(configDir);
+    }
+  }
+
+  /**
+   * Sets the container's locator
+   *
+   * Sets the two variables {@link #locatorAddress} and {@link #locatorPort}. Also calls the
+   * {@link #updateLocator()} function to write the updated locator properties to the file.
+   */
+  public void setLocator(String address, int port) throws IOException {
+    locatorAddress = address;
+    locatorPort = port;
+    updateLocator();
+  }
+
+  /**
+   * Sets the container's cache XML file
+   */
+  public void setCacheXMLFile(File cacheXMLFile) throws IOException {
+    setSystemProperty("cache-xml-file", cacheXMLFile.getAbsolutePath());
+    this.cacheXMLFile = cacheXMLFile;
+  }
+
+  /**
+   * Set a geode session replication property
+   */
+  public String setCacheProperty(String name, String value) throws IOException {
+    return cacheProperties.put(name, value);
+  }
+
+  /**
+   * Set geode distributed system property
+   */
+  public String setSystemProperty(String name, String value) throws IOException {
+    return systemProperties.put(name, value);
+  }
+
+  /**
+   * Sets the war file for this container to deploy and use
+   */
+  public void setWarFile(File warFile) {
+    this.warFile = warFile;
+  }
+
+  /**
+   * set the container's logging level
+   */
+  public void setLoggingLevel(String loggingLevel) {
+    this.loggingLevel = loggingLevel;
+
+    LocalConfiguration config = getConfiguration();
+    config.setProperty(GeneralPropertySet.LOGGING, loggingLevel);
+    container.setConfiguration(config);
+  }
+
+  public InstalledLocalContainer getContainer() {
+    return container;
+  }
+
+  public ContainerInstall getInstall() {
+    return install;
+  }
+
+  public File getWarFile() {
+    return warFile;
+  }
+
+  public String getLoggingLevel() {
+    return loggingLevel;
+  }
+
+  public LocalConfiguration getConfiguration() {
+    return container.getConfiguration();
+  }
+
+  public State getState() {
+    return container.getState();
+  }
+
+  public String getCacheProperty(String name) {
+    return cacheProperties.get(name);
+  }
+
+  public String getSystemProperty(String name) {
+    return systemProperties.get(name);
+  }
+
+  /**
+   * Get the RMI port for the container
+   *
+   * Calls {@link #getPort()} with the {@link GeneralPropertySet#RMI_PORT} option.
+   */
+  public String getRMIPort() {
+    return getPort(GeneralPropertySet.RMI_PORT);
+  }
+
+  /**
+   * Get the basic port for the container
+   *
+   * Calls {@link #getPort()} with the {@link ServletPropertySet#PORT} option.
+   */
+  public String getPort() {
+    return getPort(ServletPropertySet.PORT);
+  }
+
+  /**
+   * The container's port for the specified port type
+   */
+  public String getPort(String portType) {
+    LocalConfiguration config = getConfiguration();
+    config.applyPortOffset();
+
+    if (!container.getState().isStarted())
+      throw new IllegalStateException(
+          "Container is not started, thus a port has not yet been assigned to the container.");
+
+    return config.getPropertyValue(portType);
+  }
+
+  /**
+   * Called before each container startup
+   *
+   * This is mainly used to write properties to whatever format they need to be in for a given
+   * container before the container is started. The reason for doing this is to make sure that
+   * expensive property updates (such as writing to an XML file or building WAR files from the
+   * command line) only happen as often as they are needed. These kinds of updates usually only need
+   * to happen on container startup.
+   */
+  public abstract void writeSettings() throws Exception;
+
+  /**
+   * Human readable description of the container
+   *
+   * @return The {@link #description} variable along with the state of this {@link #container}
+   */
+  @Override
+  public String toString() {
+    return description + "_<" + container.getState() + ">";
+  }
+
+  /**
+   * Updates the address and port of the locator for this container
+   *
+   * For Client Server installations the {@link #cacheXMLFile} is updated with the new address and
+   * port. For Peer to Peer installations the locator must be specified as a system property and so
+   * is added to the {@link #systemProperties} map under the 'locators' key in the form of
+   * '{@link #locatorAddress}[{@link #locatorPort}]'.
+   */
+  private void updateLocator() throws IOException {
+    if (getInstall().isClientServer()) {
+      HashMap<String, String> attributes = new HashMap<>();
+      attributes.put("host", locatorAddress);
+      attributes.put("port", Integer.toString(locatorPort));
+
+      ContainerInstall.editXMLFile(getSystemProperty("cache-xml-file"), "locator", "pool",
+          attributes);
+    } else {
+      setSystemProperty("locators", locatorAddress + "[" + locatorPort + "]");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java
index c1269de..91ad611 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java
@@ -30,9 +30,9 @@ public class Tomcat6ClientServerTest extends TomcatClientServerTest {
   @BeforeClass
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT6,
-        TomcatInstall.TomcatConfig.CLIENT_SERVER,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat6ClientServerTest");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java
index 230ef49..0c15b15 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java
@@ -31,7 +31,7 @@ public class Tomcat6Test extends CargoTestBase {
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT6,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat6Test");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java
index 514e7be..622fb2f 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.session.tests;
 
+import java.awt.Container;
+
 import org.junit.BeforeClass;
 
 import org.apache.geode.test.dunit.DUnitEnv;
@@ -30,9 +32,9 @@ public class Tomcat7ClientServerTest extends TomcatClientServerTest {
   @BeforeClass
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT7,
-        TomcatInstall.TomcatConfig.CLIENT_SERVER,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat7ClientServerTest");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7Test.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7Test.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7Test.java
index c8039e8..a256ca4 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7Test.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7Test.java
@@ -31,7 +31,7 @@ public class Tomcat7Test extends CargoTestBase {
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT7,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat7Test");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8ClientServerTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8ClientServerTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8ClientServerTest.java
index 4e6bc1f..03f4ba5 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8ClientServerTest.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8ClientServerTest.java
@@ -30,9 +30,9 @@ public class Tomcat8ClientServerTest extends TomcatClientServerTest {
   @BeforeClass
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT8,
-        TomcatInstall.TomcatConfig.CLIENT_SERVER,
+        ContainerInstall.ConnectionType.CLIENT_SERVER,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat8ClientServerTest");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/geode/blob/6e867623/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8Test.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8Test.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8Test.java
index 6f57bf7..69a824e 100644
--- a/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8Test.java
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat8Test.java
@@ -31,7 +31,7 @@ public class Tomcat8Test extends CargoTestBase {
   public static void setupTomcatInstall() throws Exception {
     install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT8,
         ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat8Test");
-    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+    install.setDefaultLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
   }
 
   @Override


Mime
View raw message