geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject [40/50] [abbrv] geode git commit: GEODE-2901: Adding integration tests of session replication
Date Thu, 06 Jul 2017 18:27:14 GMT
GEODE-2901: Adding integration tests of session replication

Adding integration tests that test both session replications
frameworks shipped with geode. These tests use cargo to download
and launch different J2EE containers, so the tests can run
against many different containers.

This closes #584


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

Branch: refs/heads/feature/GEODE-1279
Commit: aa68f0450d6ac95e09c773bc3232cf16bcd32911
Parents: 52fe793
Author: David Anuta <david.r.anuta@gmail.com>
Authored: Fri May 26 17:07:36 2017 -0700
Committer: Dan Smith <upthewaterspout@apache.org>
Committed: Fri Jun 30 11:40:21 2017 -0700

----------------------------------------------------------------------
 .gitignore                                      |   2 +-
 .../catalina/Tomcat8DeltaSessionManager.java    |   4 +-
 .../geode/modules/session/CommandServlet.java   |   1 +
 extensions/session-testing-war/build.gradle     |  27 ++
 .../geode/modules/session/CommandServlet.java   |  94 +++++
 .../geode/modules/session/QueryCommand.java     |  34 ++
 .../src/main/webapp/WEB-INF/web.xml             |  43 ++
 geode-assembly/build.gradle                     |  15 +
 .../geode/session/tests/CargoTestBase.java      | 302 ++++++++++++++
 .../org/apache/geode/session/tests/Client.java  | 286 +++++++++++++
 .../geode/session/tests/ContainerInstall.java   | 410 +++++++++++++++++++
 .../geode/session/tests/ContainerManager.java   | 358 ++++++++++++++++
 .../tests/GenericAppServerClientServerTest.java |  44 ++
 .../session/tests/GenericAppServerInstall.java  | 247 +++++++++++
 .../session/tests/Jetty9ClientServerTest.java   |  42 ++
 .../apache/geode/session/tests/Jetty9Test.java  |  41 ++
 .../session/tests/Tomcat6ClientServerTest.java  |  42 ++
 .../apache/geode/session/tests/Tomcat6Test.java |  41 ++
 .../session/tests/Tomcat7ClientServerTest.java  |  42 ++
 .../apache/geode/session/tests/Tomcat7Test.java |  41 ++
 .../session/tests/Tomcat8ClientServerTest.java  |  42 ++
 .../apache/geode/session/tests/Tomcat8Test.java |  41 ++
 .../session/tests/TomcatClientServerTest.java   |  83 ++++
 .../geode/session/tests/TomcatInstall.java      | 399 ++++++++++++++++++
 gradle/rat.gradle                               |   4 +
 settings.gradle                                 |   1 +
 26 files changed, 2683 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 6899907..e61fd470 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,7 @@ build-eclipse/
 /tags
 out/
 
-
+.DS_store
 *.iml
 *.ipr
 *.iws

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManager.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManager.java b/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManager.java
index 16ea322..2ca4653 100644
--- a/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManager.java
+++ b/extensions/geode-modules-tomcat8/src/main/java/org/apache/geode/modules/session/catalina/Tomcat8DeltaSessionManager.java
@@ -14,14 +14,14 @@
  */
 package org.apache.geode.modules.session.catalina;
 
+import java.io.IOException;
+
 import org.apache.catalina.Context;
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.LifecycleState;
 import org.apache.catalina.Pipeline;
 import org.apache.catalina.session.StandardSession;
 
-import java.io.IOException;
-
 public class Tomcat8DeltaSessionManager extends DeltaSessionManager {
 
   /**

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/CommandServlet.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/CommandServlet.java b/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/CommandServlet.java
index a04194b..8da6cec 100644
--- a/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/CommandServlet.java
+++ b/extensions/geode-modules/src/test/java/org/apache/geode/modules/session/CommandServlet.java
@@ -71,6 +71,7 @@ public class CommandServlet extends HttpServlet {
           out.write(val);
         }
         break;
+
       case INVALIDATE:
         session = request.getSession();
         session.invalidate();

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/session-testing-war/build.gradle
----------------------------------------------------------------------
diff --git a/extensions/session-testing-war/build.gradle b/extensions/session-testing-war/build.gradle
new file mode 100644
index 0000000..688a9a7
--- /dev/null
+++ b/extensions/session-testing-war/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+apply plugin: 'war'
+
+dependencies {
+    provided 'javax.servlet:javax.servlet-api:' + project.'javax.servlet-api.version'
+}
+
+war {
+    version = ''
+}
+
+disableMavenPublishing()

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/CommandServlet.java
----------------------------------------------------------------------
diff --git a/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/CommandServlet.java b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/CommandServlet.java
new file mode 100644
index 0000000..dabd45b
--- /dev/null
+++ b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/CommandServlet.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.modules.session;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ *
+ */
+public class CommandServlet extends HttpServlet {
+
+  private ServletContext context;
+
+  /**
+   * The standard servlet method overridden.
+   *
+   * @param request
+   * @param response
+   * @throws IOException
+   */
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response)
+      throws IOException, ServletException {
+
+    QueryCommand cmd = QueryCommand.UNKNOWN;
+    String param = request.getParameter("param");
+    String value = request.getParameter("value");
+    PrintWriter out = response.getWriter();
+
+    String cmdStr = request.getParameter("cmd");
+    if (cmdStr != null) {
+      cmd = QueryCommand.valueOf(cmdStr);
+    }
+
+    HttpSession session;
+
+    switch (cmd) {
+      case SET:
+        session = request.getSession();
+        session.setAttribute(param, value);
+        break;
+      case SET_MAX_INACTIVE:
+        session = request.getSession();
+        session.setMaxInactiveInterval(Integer.valueOf(value));
+        break;
+      case GET:
+        session = request.getSession();
+        String val = (String) session.getAttribute(param);
+        if (val != null) {
+          out.write(val);
+        }
+        break;
+      case REMOVE:
+        session = request.getSession();
+        session.removeAttribute(param);
+        break;
+      case INVALIDATE:
+        session = request.getSession();
+        session.invalidate();
+        break;
+    }
+  }
+
+  /**
+   * Save a reference to the ServletContext for later use.
+   *
+   * @param config
+   */
+  @Override
+  public void init(ServletConfig config) {
+    this.context = config.getServletContext();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/QueryCommand.java
----------------------------------------------------------------------
diff --git a/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/QueryCommand.java b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/QueryCommand.java
new file mode 100644
index 0000000..a3a63c3
--- /dev/null
+++ b/extensions/session-testing-war/src/main/java/org/apache/geode/modules/session/QueryCommand.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.modules.session;
+
+/**
+ * Basic commands to pass to our test servlet
+ */
+public enum QueryCommand {
+
+  SET,
+
+  SET_MAX_INACTIVE,
+
+  GET,
+
+  REMOVE,
+
+  INVALIDATE,
+
+  UNKNOWN;
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml b/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1ed33cd
--- /dev/null
+++ b/extensions/session-testing-war/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0"
+         metadata-complete="true">
+
+  <display-name>Geode Test war</display-name>
+
+  <description>
+    Test war file for geode session management
+  </description>
+
+    <servlet>
+    <description>
+      Some test servlet
+    </description>
+    <servlet-name>cmd-servlet</servlet-name>
+    <servlet-class>org.apache.geode.modules.session.CommandServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>cmd-servlet</servlet-name>
+    <url-pattern>/*</url-pattern>
+  </servlet-mapping>
+
+</web-app>

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/geode-assembly/build.gradle
----------------------------------------------------------------------
diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle
index 3af7632..e5e96d8 100755
--- a/geode-assembly/build.gradle
+++ b/geode-assembly/build.gradle
@@ -74,6 +74,20 @@ dependencies {
   testCompile 'org.apache.httpcomponents:httpclient:' + project.'httpclient.version'
   testCompile 'org.apache.httpcomponents:httpcore:' + project.'httpcore.version'
   testCompile 'com.google.guava:guava:' + project.'guava.version'
+  testCompile 'org.apache.commons:commons-exec:' + project.'commons-exec.version'
+
+  testCompile group: 'org.codehaus.cargo', name: 'cargo-core-uberjar', version: '1.6.3'
+
+  testCompile (project(':extensions/geode-modules')) {
+    // Remove everything related to Tomcat
+    exclude group: 'org.apache.tomcat'
+  }
+  testCompile (project(':extensions/geode-modules-session-internal')) {
+    // Remove everything related to Tomcat
+    exclude group: 'org.apache.tomcat'
+  }
+
+  testCompile project(':extensions/session-testing-war')
 
   testRuntime files("${System.getProperty('java.home')}/../lib/tools.jar")
   testRuntime files("$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar")
@@ -404,6 +418,7 @@ flakyTest dependOnInstalledProduct
 build.dependsOn installDist
 
 installDist.dependsOn ':extensions/geode-modules-assembly:dist'
+distributedTest.dependsOn ':extensions/session-testing-war:war'
 
 /**Print the names of all jar files in a fileTree */
 def printJars(tree) {

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..9e5e5c4
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/CargoTestBase.java
@@ -0,0 +1,302 @@
+/*
+ * 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.IOException;
+import java.net.URISyntaxException;
+
+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.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
+import org.apache.geode.test.junit.categories.DistributedTest;
+
+/**
+ * Base class for test of session replication.
+ *
+ * This class contains all of the tests of session replication functionality. Subclasses of this
+ * class configure different containers in order to run these tests against specific containers.
+ */
+@Category(DistributedTest.class)
+public abstract class CargoTestBase extends JUnit4CacheTestCase {
+  @Rule
+  public transient TestName testName = new TestName();
+
+  public transient Client client;
+  public transient ContainerManager manager;
+
+  public abstract ContainerInstall getInstall();
+
+  @Before
+  public void setup() throws IOException {
+    client = new Client();
+    manager = new ContainerManager();
+
+    manager.setTestName(testName.getMethodName());
+    manager.addContainers(2, getInstall());
+  }
+
+  @After
+  public void stop() throws IOException {
+    manager.stopAllActiveContainers();
+    manager.cleanUp();
+  }
+
+  /**
+   * Test that when multiple containers are using session replication, all of the containers will
+   * use the same session cookie for the same client.
+   */
+  @Test
+  public void containersShouldReplicateCookies() throws IOException, URISyntaxException {
+    manager.startAllInactiveContainers();
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response resp = client.get(null);
+    String cookie = resp.getSessionCookie();
+
+    for (int i = 1; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(null);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+    }
+  }
+
+  /**
+   * Test that when a session attribute is set in one container, it is replicated to other
+   * containers
+   */
+  @Test
+  public void containersShouldHavePersistentSessionData() throws IOException, URISyntaxException {
+    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++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+  }
+
+  /**
+   * Test that when a container fails, session attributes that were previously set in that container
+   * are still available in other containers
+   */
+  @Test
+  public void failureShouldStillAllowOtherContainersDataAccess()
+      throws IOException, URISyntaxException {
+    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();
+
+    manager.stopContainer(0);
+
+    for (int i = 1; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+  }
+
+  /**
+   * Test that invalidating a session in one container invalidates the session in all containers.
+   */
+  @Test
+  public void invalidationShouldRemoveValueAccessForAllContainers()
+      throws IOException, URISyntaxException {
+    manager.startAllInactiveContainers();
+
+    String key = "value_testInvalidate";
+    String value = "Foo";
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response resp = client.set(key, value);
+    String cookie = resp.getSessionCookie();
+
+    client.invalidate();
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("", resp.getResponse());
+    }
+  }
+
+  /**
+   * Test that if a session is not used within the expiration time, it is expired and removed from
+   * all containers
+   */
+  @Test
+  public void containersShouldExpireInSetTimeframe()
+      throws IOException, URISyntaxException, InterruptedException {
+    manager.startAllInactiveContainers();
+
+    String key = "value_testSessionExpiration";
+    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++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+
+    client.setMaxInactive(1);
+
+    Thread.sleep(5000);
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("", resp.getResponse());
+    }
+  }
+
+
+  /**
+   * Test that if one container is accessing a session, that will prevent the session from expiring
+   * in all containers.
+   */
+  @Test
+  public void containersShouldShareSessionExpirationReset()
+      throws URISyntaxException, IOException, InterruptedException {
+    manager.startAllInactiveContainers();
+
+    int timeToExp = 5;
+    String key = "value_testSessionExpiration";
+    String value = "Foo";
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response resp = client.set(key, value);
+    String cookie = resp.getSessionCookie();
+
+    client.setMaxInactive(timeToExp);
+
+    long startTime = System.currentTimeMillis();
+    long curTime = System.currentTimeMillis();
+    // Run for 2 times the set expiration time
+    while (curTime - startTime < timeToExp * 2000) {
+      client.get(key);
+      Thread.sleep(500);
+      curTime = System.currentTimeMillis();
+    }
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+  }
+
+  /**
+   * Test that if a session attribute is removed in one container, it is removed from all containers
+   */
+  @Test
+  public void containersShouldShareDataRemovals() throws IOException, URISyntaxException {
+    manager.startAllInactiveContainers();
+
+    String key = "value_testSessionRemove";
+    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++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    client.remove(key);
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(
+          "Was expecting an empty response after removal. Double check to make sure that the enableLocalCache cacheProperty is set to false. This test is unreliable on servers which use a local cache.",
+          "", resp.getResponse());
+    }
+  }
+
+  @Test
+  public void newContainersShouldShareDataAccess() throws IOException, URISyntaxException {
+    manager.startAllInactiveContainers();
+
+    String key = "value_testSessionAdd";
+    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++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+    int numContainers = manager.numContainers();
+
+    manager.addContainer(getInstall());
+    manager.startAllInactiveContainers();
+
+    assertEquals(numContainers + 1, manager.numContainers());
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals(value, resp.getResponse());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/geode-assembly/src/test/java/org/apache/geode/session/tests/Client.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/Client.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/Client.java
new file mode 100644
index 0000000..9b458d0
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Client.java
@@ -0,0 +1,286 @@
+/*
+ * 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 org.apache.geode.modules.session.CommandServlet;
+import org.apache.geode.modules.session.QueryCommand;
+import org.apache.http.Header;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+/**
+ * A simple http client that talks to a server running the session-testing-war.
+ *
+ * This client sends commands to the {@link CommandServlet} over http to modify session properties
+ * and returns the results. The client has support for connecting to multiple servers and sending
+ * the session cookie returned by one server to other servers, to emulate the behavior of a client
+ * talking to the servers through a load balancer.
+ *
+ * The client currently only targets servers running on "localhost"
+ *
+ * To set the server this client is targeting, use {@link #setPort}.
+ */
+public class Client {
+  private static final String HOST = "localhost";
+  private int port = 8080;
+  private String cookie;
+  private HttpContext context;
+
+  private URIBuilder reqURIBuild;
+  private CloseableHttpClient httpclient;
+
+  public Client() {
+    reqURIBuild = new URIBuilder();
+    reqURIBuild.setScheme("http");
+
+    httpclient = HttpClients.createDefault();
+    context = new BasicHttpContext();
+
+    cookie = null;
+  }
+
+  /**
+   * Change the server that the client is targeting.
+   * 
+   * @param port the port that the server is listening on
+   */
+  public void setPort(int port) {
+    this.port = port;
+  }
+
+  /**
+   * Get the value of a session attribute on the server
+   */
+  public Response get(String key) throws IOException, URISyntaxException {
+    return get(key, true);
+  }
+
+  /**
+   * Get the value of a session attribute on the server
+   */
+  public Response get(String key, boolean storeRespCookie) throws IOException, URISyntaxException {
+    resetURI();
+    reqURIBuild.setParameter("cmd", QueryCommand.GET.name());
+    reqURIBuild.setParameter("param", key);
+
+    return doRequest(new HttpGet(reqURIBuild.build()), storeRespCookie);
+  }
+
+  /**
+   * Set the value of a session attribute on the server
+   */
+  public Response set(String key, String value) throws IOException, URISyntaxException {
+    return set(key, value, true);
+  }
+
+  /**
+   * Set the value of a session attribute on the server
+   */
+  public Response set(String key, String value, boolean storeRespCookie)
+      throws IOException, URISyntaxException {
+    resetURI();
+    reqURIBuild.setParameter("cmd", QueryCommand.SET.name());
+    reqURIBuild.setParameter("param", key);
+    reqURIBuild.setParameter("value", value);
+
+    return doRequest(new HttpGet(reqURIBuild.build()), storeRespCookie);
+  }
+
+  /**
+   * Remove the session attribute on the server
+   * 
+   * @param key - the session attribute to remove
+   */
+  public Response remove(String key) throws IOException, URISyntaxException {
+    return remove(key, true);
+  }
+
+  /**
+   * Remove the session attribute on the server
+   * 
+   * @param key - the session attribute to remove
+   * @param storeRespCookie - whether or not to store the session cookie of this request
+   */
+  public Response remove(String key, boolean storeRespCookie)
+      throws URISyntaxException, IOException {
+    resetURI();
+    reqURIBuild.setParameter("cmd", QueryCommand.REMOVE.name());
+    reqURIBuild.setParameter("param", key);
+
+    return doRequest(new HttpGet(reqURIBuild.build()), storeRespCookie);
+  }
+
+  /**
+   * Invalidate this clients session on the server
+   */
+  public Response invalidate() throws IOException, URISyntaxException {
+    return invalidate(true);
+  }
+
+  /**
+   * Invalidate this clients session on the server
+   */
+  public Response invalidate(boolean storeRespCookie) throws IOException, URISyntaxException {
+    resetURI();
+    reqURIBuild.setParameter("cmd", QueryCommand.INVALIDATE.name());
+    reqURIBuild.setParameter("param", "null");
+
+    return doRequest(new HttpGet(reqURIBuild.build()), storeRespCookie);
+  }
+
+  /**
+   * Set the maximum inactive interval for this client's session on the server.
+   *
+   * If this time interval elapses without activity on the session, the session will expire.
+   *
+   * @param time - Time in seconds until the session should expire
+   */
+  public Response setMaxInactive(int time) throws IOException, URISyntaxException {
+    return setMaxInactive(time, true);
+  }
+
+  /**
+   * Set the maximum inactive interval for this client's session on the server.
+   *
+   * If this time interval elapses without activity on the session, the session will expire.
+   *
+   * @param time - Time in seconds until the session should expire
+   */
+  public Response setMaxInactive(int time, boolean storeRespCookie)
+      throws IOException, URISyntaxException {
+    resetURI();
+    reqURIBuild.setParameter("cmd", QueryCommand.SET_MAX_INACTIVE.name());
+    reqURIBuild.setParameter("value", Integer.toString(time));
+
+    return doRequest(new HttpGet(reqURIBuild.build()), storeRespCookie);
+  }
+
+  private void resetURI() {
+    reqURIBuild.setHost(HOST + ":" + port);
+    reqURIBuild.clearParameters();
+  }
+
+  /**
+   * Sends a request to the server and returns a custom response object with the result
+   * 
+   * @param storeRespCookie if true, retain the value of a "Set-Cookie" header returned in the
+   *        response.
+   */
+  private Response doRequest(HttpGet req, boolean storeRespCookie) throws IOException {
+    addCookieHeader(req);
+
+    CloseableHttpResponse resp = httpclient.execute(req, context);
+
+    boolean isNew = true;
+    String reqCookie = getCookieHeader(resp);
+    if (reqCookie == null) {
+      isNew = false;
+      reqCookie = this.cookie;
+    } else if (storeRespCookie) {
+      this.cookie = reqCookie;
+    }
+
+    StatusLine status = resp.getStatusLine();
+    if (status.getStatusCode() != 200) {
+      throw new IOException("Http request failed. " + status);
+    }
+
+    Response response = new Response(reqCookie, EntityUtils.toString(resp.getEntity()), isNew);
+    resp.close();
+    return response;
+  }
+
+  private void addCookieHeader(HttpGet req) {
+    // Set the cookie header
+    if (cookie != null) {
+      BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", this.cookie);
+      cookie.setDomain(req.getURI().getHost());
+      cookie.setPath("/");
+
+      BasicCookieStore cookieStore = new BasicCookieStore();
+      cookieStore.addCookie(cookie);
+
+      context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
+    }
+  }
+
+  private String getCookieHeader(CloseableHttpResponse resp) {
+    Header lastHeader = resp.getLastHeader("Set-Cookie");
+
+    if (lastHeader == null) {
+      return null;
+    }
+    return lastHeader.getElements()[0].getValue();
+  }
+
+  /**
+   * A response received from the server.
+   *
+   * Currently contains the text value of the response body, as well as the session cookie.
+   */
+  public class Response {
+    private final String sessionCookie;
+    private final String response;
+    private final boolean isNew;
+
+    public Response(String sessionCookie, String response, boolean isNew) {
+      this.sessionCookie = sessionCookie;
+      this.response = response;
+      this.isNew = isNew;
+    }
+
+    /**
+     * The session cookie associated with this client.
+     */
+    public String getSessionCookie() {
+      return sessionCookie;
+    }
+
+    /**
+     *
+     * The String value of the response body.
+     */
+    public String getResponse() {
+      return response;
+    }
+
+    /**
+     * @return true if this response contained a "Set-Cookie" header, indicating that the server is
+     *         setting a new value for the session cookie.
+     */
+    public boolean isNew() {
+      return isNew;
+    }
+
+    @Override
+    public String toString() {
+      return "Response{" + "sessionCookie='" + sessionCookie + '\'' + ", response='" + response
+          + '\'' + '}';
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..65aefa4
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerInstall.java
@@ -0,0 +1,410 @@
+/*
+ * 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.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;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+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;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.management.internal.configuration.utils.ZipUtils;
+
+/**
+ * Base class for handling downloading and configuring J2EE containers.
+ *
+ * This class contains common logic for downloading and configuring J2EE containers with cargo, and
+ * some common methods for applying geode session replication configuration to those containers.
+ *
+ * Subclasses provide installation of specific containers.
+ */
+public abstract class ContainerInstall {
+  public static final Logger logger = LogService.getLogger();
+
+  private final String INSTALL_PATH;
+  public static final String DEFAULT_INSTALL_DIR = "/tmp/cargo_containers/";
+  public static final String GEODE_BUILD_HOME = System.getenv("GEODE_HOME");
+
+  public HashMap<String, String> cacheProperties;
+  public HashMap<String, String> systemProperties;
+
+  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();
+    INSTALL_PATH = installer.getHome();
+    logger.info("Installed container into " + getInstallPath());
+
+    cacheProperties = new HashMap<>();
+    systemProperties = new HashMap<>();
+  }
+
+  /**
+   * The directory in which this container is installed.
+   */
+  public String getInstallPath() {
+    return INSTALL_PATH;
+  }
+
+  /**
+   * Called by the installation before 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 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.
+   */
+  public abstract void writeProperties() throws Exception;
+
+  /**
+   * Cargo's specific string to identify the container
+   */
+  public abstract String getContainerId();
+
+  /**
+   * A human readable description of the container
+   */
+  public abstract String getContainerDescription();
+
+  /**
+   * Configure the geode session replication install in this container to connect to the given
+   * locator.
+   */
+  public abstract void setLocator(String address, int port) throws Exception;
+
+  /**
+   * 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.
+   */
+  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());
+  }
+
+  /**
+   * Set a geode session replication property. For example enableLocalCache.
+   */
+  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);
+  }
+
+  /**
+   * Get the specified cache property for an install
+   */
+  public String getCacheProperty(String name) {
+    return cacheProperties.get(name);
+  }
+
+  /**
+   * Get the specified system property for an install
+   */
+  public String getSystemProperty(String name) {
+    return systemProperties.get(name);
+  }
+
+  /**
+   * Callback to allow this install to update the configuration before it is launched
+   */
+  public void modifyConfiguration(LocalConfiguration configuration) {}
+
+  protected String findSessionTestingWar() {
+    // Start out searching directory above current
+    String curPath = "../";
+
+    // Looking for extensions folder
+    final String warModuleDirName = "extensions";
+    File warModuleDir = null;
+
+    // While directory searching for is not found
+    while (warModuleDir == null) {
+      // Try to find the find the directory in the current directory
+      File[] files = new File(curPath).listFiles();
+      for (File file : files) {
+        if (file.isDirectory() && file.getName().equals(warModuleDirName)) {
+          warModuleDir = file;
+          break;
+        }
+      }
+
+      // Keep moving up until you find it
+      curPath += "../";
+    }
+
+    // Return path to extensions plus hardcoded path from there to the WAR
+    return warModuleDir.getAbsolutePath()
+        + "/session-testing-war/build/libs/session-testing-war.war";
+  }
+
+  /**
+   * Return the session testing war file to use for this container.
+   *
+   * 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.
+   */
+  public WAR getDeployableWAR() {
+    return new WAR(findSessionTestingWar());
+  }
+
+  protected static String findAndExtractModule(String geodeBuildHome, String moduleName)
+      throws IOException {
+    String modulePath = null;
+    String modulesDir = geodeBuildHome + "/tools/Modules/";
+
+    boolean archive = false;
+    logger.info("Trying to access build dir " + modulesDir);
+
+    // Search directory for tomcat module folder/zip
+    for (File file : (new File(modulesDir)).listFiles()) {
+
+      if (file.getName().toLowerCase().contains(moduleName)) {
+        modulePath = file.getAbsolutePath();
+
+        archive = !file.isDirectory();
+        if (!archive)
+          break;
+      }
+    }
+
+    // Unzip if it is a zip file
+    if (archive) {
+      if (!FilenameUtils.getExtension(modulePath).equals("zip")) {
+        throw new IOException("Bad module archive " + modulePath);
+      }
+
+      ZipUtils.unzip(modulePath, modulePath.substring(0, modulePath.length() - 4));
+
+      modulePath = modulePath.substring(0, modulePath.length() - 4);
+    }
+
+    // No module found within directory throw IOException
+    if (modulePath == null)
+      throw new IOException("No module found in " + modulesDir);
+    return modulePath;
+  }
+
+  /**
+   * 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
+   * @param append whether or not to append the given property value. If true appends the given
+   *        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,
+      boolean append) throws Exception {
+    FileInputStream input = new FileInputStream(filePath);
+    Properties properties = new Properties();
+    properties.load(input);
+
+    String val;
+    if (append)
+      val = properties.getProperty(propertyName) + propertyValue;
+    else
+      val = propertyValue;
+
+    properties.setProperty(propertyName, val);
+    properties.store(new FileOutputStream(filePath), null);
+
+    logger.info("Modified container Property file " + filePath);
+  }
+
+  protected 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,
+      HashMap<String, String> attributes) {
+    editXMLFile(XMLPath, null, tagName, parentTagName, attributes, false);
+  }
+
+  protected 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
+   * 
+   * @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.
+   * @param tagName The name of the xml element to edit
+   * @param parentTagName The parent element of the element we should edit
+   * @param attributes the xml attributes for the element to edit
+   * @param writeOnSimilarAttributeNames If true, find an existing element with the same set of
+   *        attributes as the attributes parameter, and modifies the attributes of that element,
+   *        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
+    try {
+      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) {
+        Element e = doc.createElement(tagName);
+        // Set id attribute
+        if (tagId != null)
+          e.setAttribute("id", tagId);
+        // Set other attributes
+        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);
+      }
+
+      // Write updated XML file
+      TransformerFactory transformerFactory = TransformerFactory.newInstance();
+      Transformer transformer = transformerFactory.newTransformer();
+      DOMSource source = new DOMSource(doc);
+      StreamResult result = new StreamResult(new File(XMLPath));
+      transformer.transform(source, result);
+
+      logger.info("Modified container XML file " + XMLPath);
+    } catch (Exception e) {
+      throw new RuntimeException("Unable to edit XML file", e);
+    }
+  }
+
+  /**
+   * Get the location of this installations configuration home
+   */
+  public String getContainerConfigHome() {
+    return "/tmp/cargo_configs/" + getContainerDescription();
+  }
+
+  /**
+   * Get the logging level of this install
+   */
+  public String getLoggingLevel() {
+    return LoggingLevel.HIGH.getLevel();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..2dba38a
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/ContainerManager.java
@@ -0,0 +1,358 @@
+/*
+ * 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.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;
+
+/**
+ * Manages multiple J2EE containers using cargo.
+ *
+ * Provides methods to start and stop J2EE containers and obtain the http ports those containers are
+ * listening on.
+ */
+public class ContainerManager {
+  private static final Logger logger = LogService.getLogger();
+
+  private ArrayList<InstalledLocalContainer> containers;
+  private ArrayList<ContainerInstall> installs;
+
+  private String testName;
+
+  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
+   */
+  public void setTestName(String testName) {
+    this.testName = testName;
+  }
+
+  /**
+   * Add a new container to manage using the specified installation
+   *
+   * The container will not be running until one of the start methods is called.
+   */
+  public InstalledLocalContainer addContainer(ContainerInstall install) throws IOException {
+    return addContainer(install, containers.size());
+  }
+
+  /**
+   * Add multiple containers to manage using the specified installation.
+   *
+   * The containers will not be running until one of the start methods is called.
+   */
+  public void addContainers(int numContainers, ContainerInstall install) throws IOException {
+    for (int i = 0; i < numContainers; i++)
+      addContainer(install);
+  }
+
+  /**
+   * Return the http port the given container is listening on, if the container is running
+   * 
+   * @throws IllegalStateException if the container is not running.
+   */
+  public String getContainerPort(int index) {
+    return getContainerPort(getContainer(index));
+  }
+
+  private String getContainerPort(InstalledLocalContainer container) {
+    LocalConfiguration config = container.getConfiguration();
+    config.applyPortOffset();
+
+    if (!container.getState().isStarted()) {
+      throw new IllegalStateException("Port has not yet been assigned to container");
+    }
+    return config.getPropertyValue(ServletPropertySet.PORT);
+  }
+
+  /**
+   * @return the number of containers managed
+   */
+  public int numContainers() {
+    return containers.size();
+  }
+
+  public ArrayList<Integer> getContainerIndexesWithState(String state) {
+    ArrayList<Integer> indexes = new ArrayList<>();
+    for (int i = 0; i < numContainers(); i++) {
+      if (state.equals(State.STARTED.toString()) || state.equals(State.STOPPED.toString())
+          || state.equals(State.STARTED.toString()) || state.equals(State.STOPPING.toString())
+          || state.equals(State.UNKNOWN.toString())) {
+        if (getContainer(i).getState().toString().equals(state))
+          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);
+    }
+    return indexes;
+  }
+
+  /**
+   * Return the cargo container of all of the containers in the given state
+   */
+  public ArrayList<InstalledLocalContainer> getContainersWithState(String state) {
+    ArrayList<InstalledLocalContainer> 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.
+   */
+  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;
+  }
+
+  /**
+   * Start the given container
+   */
+  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());
+    }
+  }
+
+  /**
+   * Stop the given container
+   */
+  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);
+  }
+
+  /**
+   * Start all containers that are not currently running.
+   */
+  public void startAllInactiveContainers() {
+    startContainers(getInactiveContainerIndexes());
+  }
+
+  /**
+   * Stop all containers that are currently running.
+   */
+  public void stopAllActiveContainers() {
+    stopContainers(getActiveContainerIndexes());
+  }
+
+  public void removeContainer(int index) {
+    stopContainer(index);
+    containers.remove(index);
+    installs.remove(index);
+  }
+
+  /**
+   * Runs {@link #clean} on all containers
+   */
+  public void cleanUp() throws IOException {
+    for (int i = 0; i < numContainers(); i++)
+      clean(i);
+  }
+
+  /**
+   * Deletes the configuration directory for the specified container
+   */
+  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));
+  }
+
+  /**
+   * 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
+   */
+  private String getUniqueContainerDescription(int index, ContainerInstall install) {
+    return String.join("_", Arrays.asList(install.getContainerDescription(),
+        Integer.toString(index), testName, Long.toString(System.nanoTime())));
+  }
+
+  /**
+   * Create a container to manage, given an installation.
+   */
+  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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerClientServerTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerClientServerTest.java b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerClientServerTest.java
new file mode 100644
index 0000000..08f9978
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerClientServerTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.geode.cache.Cache;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.test.dunit.Host;
+import org.apache.geode.test.dunit.VM;
+import org.junit.Before;
+
+/**
+ * Extends the {@link CargoTestBase} class to support client server tests of generic app servers
+ *
+ * Currently being used to test Jetty 9 containers in client server mode.
+ */
+public abstract class GenericAppServerClientServerTest extends CargoTestBase {
+  /**
+   * Starts the server for the client containers to connect to while testing.
+   */
+  @Before
+  public void startServers() throws InterruptedException {
+    Host host = Host.getHost(0);
+    VM vm0 = host.getVM(0);
+    vm0.invoke(() -> {
+      Cache cache = getCache();
+      CacheServer server = cache.addCacheServer();
+      server.setPort(0);
+      server.start();
+    });
+    Thread.sleep(5000);
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..f5072bb
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/GenericAppServerInstall.java
@@ -0,0 +1,247 @@
+/*
+ * 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.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.
+ *
+ * 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.
+ *
+ * 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.
+ */
+public class GenericAppServerInstall extends ContainerInstall {
+
+  /**
+   * Get the download URL of a generic app server using hardcoded keywords
+   *
+   * Currently the only supported keyword instance is JETTY9
+   */
+  public enum Server {
+    JETTY9(
+        "http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.5.v20170502/jetty-distribution-9.4.5.v20170502.zip",
+        "jetty9x");
+
+    private String downloadURL;
+    private String containerId;
+
+    Server(String downloadURL, String containerId) {
+      this.downloadURL = downloadURL;
+      this.containerId = containerId;
+    }
+
+    public String getDownloadURL() {
+      return downloadURL;
+    }
+
+    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;
+    }
+  }
+
+  private File warFile;
+  private CacheType cacheType;
+  private Server server;
+
+  private final String appServerModulePath;
+
+  public GenericAppServerInstall(Server server) throws IOException, InterruptedException {
+    this(server, CacheType.PEER_TO_PEER, DEFAULT_INSTALL_DIR);
+  }
+
+  public GenericAppServerInstall(Server server, String installDir)
+      throws IOException, InterruptedException {
+    this(server, CacheType.PEER_TO_PEER, installDir);
+  }
+
+  public GenericAppServerInstall(Server server, CacheType cacheType)
+      throws IOException, InterruptedException {
+    this(server, cacheType, DEFAULT_INSTALL_DIR);
+  }
+
+  /**
+   * 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)
+      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();
+  }
+
+  /**
+   * Build the command list used to modify/build the WAR file
+   */
+  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()));
+
+    Process process = builder.start();
+
+    int exitCode = process.waitFor();
+    if (exitCode != 0) {
+      throw new IOException("Unable to run modify_war script: " + builder.command());
+    }
+  }
+
+  /**
+   * AppServer specific property updater
+   *
+   * 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()}.
+   */
+  @Override
+  public void writeProperties() throws Exception {
+    modifyWarFile();
+  }
+
+  /**
+   * @see ContainerInstall#getContainerId()
+   */
+  @Override
+  public String getContainerId() {
+    return server.getContainerId();
+  }
+
+  /**
+   * @see ContainerInstall#getContainerDescription()
+   */
+  @Override
+  public String getContainerDescription() {
+    return server.name() + "_" + cacheType.name();
+  }
+
+  /**
+   * Sets the locator for this container
+   *
+   * 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()
+   */
+  @Override
+  public WAR getDeployableWAR() {
+    return new WAR(warFile.getAbsolutePath());
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..5cd7726
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9ClientServerTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.junit.BeforeClass;
+
+import org.apache.geode.test.dunit.DUnitEnv;
+
+/**
+ * Jetty 9 Client Server tests
+ *
+ * Runs all the tests in {@link CargoTestBase} on the Jetty 9 install, setup in the
+ * {@link #setupJettyInstall()} method before tests are run.
+ */
+public class Jetty9ClientServerTest extends GenericAppServerClientServerTest {
+  private static ContainerInstall install;
+
+  @BeforeClass
+  public static void setupJettyInstall() throws Exception {
+    install = new GenericAppServerInstall(GenericAppServerInstall.Server.JETTY9,
+        GenericAppServerInstall.CacheType.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Jetty9ClientServerTest");
+    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+  }
+
+  @Override
+  public ContainerInstall getInstall() {
+    return install;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..813362b
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Jetty9Test.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.junit.BeforeClass;
+
+import org.apache.geode.test.dunit.DUnitEnv;
+
+/**
+ * Jetty 9 Peer to Peer tests
+ *
+ * Runs all the tests in {@link CargoTestBase} on the Jetty 9 install, setup in the
+ * {@link #setupJettyInstall()} method before tests are run.
+ */
+public class Jetty9Test extends CargoTestBase {
+  private static ContainerInstall install;
+
+  @BeforeClass
+  public static void setupJettyInstall() throws Exception {
+    install = new GenericAppServerInstall(GenericAppServerInstall.Server.JETTY9,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Jetty9Test");
+    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+  }
+
+  @Override
+  public ContainerInstall getInstall() {
+    return install;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..c1269de
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6ClientServerTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.junit.BeforeClass;
+
+import org.apache.geode.test.dunit.DUnitEnv;
+
+/**
+ * Tomcat 6 Client Server test
+ *
+ * Runs all the tests in {@link CargoTestBase} on the Tomcat 6 install, setup in the
+ * {@link #setupTomcatInstall()} method before tests are run.
+ */
+public class Tomcat6ClientServerTest extends TomcatClientServerTest {
+  private static ContainerInstall install;
+
+  @BeforeClass
+  public static void setupTomcatInstall() throws Exception {
+    install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT6,
+        TomcatInstall.TomcatConfig.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat6ClientServerTest");
+    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+  }
+
+  @Override
+  public ContainerInstall getInstall() {
+    return install;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..230ef49
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat6Test.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.junit.BeforeClass;
+
+import org.apache.geode.test.dunit.DUnitEnv;
+
+/**
+ * Tomcat 6 Peer to Peer tests
+ *
+ * Runs all the tests in {@link CargoTestBase} on the Tomcat 6 install, setup in the
+ * {@link #setupTomcatInstall()} method before tests are run.
+ */
+public class Tomcat6Test extends CargoTestBase {
+  private static ContainerInstall install;
+
+  @BeforeClass
+  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());
+  }
+
+  @Override
+  public ContainerInstall getInstall() {
+    return install;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/aa68f045/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
new file mode 100644
index 0000000..514e7be
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/session/tests/Tomcat7ClientServerTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.junit.BeforeClass;
+
+import org.apache.geode.test.dunit.DUnitEnv;
+
+/**
+ * Tomcat 7 Client Server tests
+ *
+ * Runs all the tests in {@link CargoTestBase} on the Tomcat 7 install, setup in the
+ * {@link #setupTomcatInstall()} method before tests are run.
+ */
+public class Tomcat7ClientServerTest extends TomcatClientServerTest {
+  private static ContainerInstall install;
+
+  @BeforeClass
+  public static void setupTomcatInstall() throws Exception {
+    install = new TomcatInstall(TomcatInstall.TomcatVersion.TOMCAT7,
+        TomcatInstall.TomcatConfig.CLIENT_SERVER,
+        ContainerInstall.DEFAULT_INSTALL_DIR + "Tomcat7ClientServerTest");
+    install.setLocator(DUnitEnv.get().getLocatorAddress(), DUnitEnv.get().getLocatorPort());
+  }
+
+  @Override
+  public ContainerInstall getInstall() {
+    return install;
+  }
+}


Mime
View raw message