geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jbarr...@apache.org
Subject [geode] branch develop updated: GEODE-6215: Making session meta-data region distributed (#3012)
Date Wed, 19 Dec 2018 21:08:35 GMT
This is an automated email from the ASF dual-hosted git repository.

jbarrett pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 947c4db  GEODE-6215: Making session meta-data region distributed (#3012)
947c4db is described below

commit 947c4db09d6463d810d5c6799ec0b22451ea6e1f
Author: Jacob Barrett <jbarrett@pivotal.io>
AuthorDate: Wed Dec 19 13:08:25 2018 -0800

    GEODE-6215: Making session meta-data region distributed (#3012)
    
    The meta data region used for session replication was converted to
    Scope.LOCAL by mistake in 7ca571c5. This broke the logic to
    automatically create the session region on all peers when starting up
    the session replication service.
    
    Changing the scope back to DISTRIBUTED_ACK for this metadata region, and
    adding tests that session region creation works.
    
    * Rolling upgrade test for session modules
    * Disabling rolling upgrade test on java 11. The
      Tomcat8ClientServerRollingUpgradeTest won't work with geode versions less
      and 1.8 on java 11.
    
    Co-Authored-By: Dan Smith <dsmith@pivotal.io>
---
 extensions/geode-modules/build.gradle              |   7 +
 .../util/ClientServerSessionCacheDUnitTest.java    | 267 +++++++++++++++++++
 .../geode/modules/util/CreateRegionFunction.java   |   6 +-
 .../geode/modules/util/SessionCustomExpiry.java    |   4 +
 .../geode/session/tests/ContainerManager.java      |   7 +
 .../session/tests/TomcatClientServerTest.java      |  18 +-
 .../Tomcat8ClientServerRollingUpgradeTest.java     | 293 +++++++++++++++++++++
 .../internal/ClusterDistributionManager.java       |   5 +
 .../distributed/internal/DistributionManager.java  |   2 +
 .../internal/LonerDistributionManager.java         |   5 +
 .../cache/xmlcache/RegionAttributesCreation.java   |  10 +-
 11 files changed, 615 insertions(+), 9 deletions(-)

diff --git a/extensions/geode-modules/build.gradle b/extensions/geode-modules/build.gradle
index 662d71d..6fa960c 100644
--- a/extensions/geode-modules/build.gradle
+++ b/extensions/geode-modules/build.gradle
@@ -52,6 +52,13 @@ dependencies {
 
   integrationTestRuntime('org.apache.tomcat:coyote:' + project.'tomcat6.version')
   integrationTestRuntime('xerces:xercesImpl')
+
+  distributedTestCompile(project(':geode-junit')) {
+    exclude module: 'geode-core'
+  }
+  distributedTestCompile(project(':geode-dunit')) {
+    exclude module: 'geode-core'
+  }
 }
 
 disableMavenPublishing()
diff --git a/extensions/geode-modules/src/distributedTest/java/org/apache/geode/modules/util/ClientServerSessionCacheDUnitTest.java
b/extensions/geode-modules/src/distributedTest/java/org/apache/geode/modules/util/ClientServerSessionCacheDUnitTest.java
new file mode 100644
index 0000000..827c8cd
--- /dev/null
+++ b/extensions/geode-modules/src/distributedTest/java/org/apache/geode/modules/util/ClientServerSessionCacheDUnitTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.util;
+
+import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collection;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.juli.logging.Log;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionAttributes;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.Scope;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.client.ClientCacheFactory;
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.distributed.internal.MembershipListener;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.modules.session.catalina.ClientServerSessionCache;
+import org.apache.geode.modules.session.catalina.SessionManager;
+import org.apache.geode.test.dunit.DistributedTestUtils;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.rules.CacheRule;
+import org.apache.geode.test.dunit.rules.ClientCacheRule;
+import org.apache.geode.test.dunit.rules.DistributedRule;
+
+public class ClientServerSessionCacheDUnitTest implements Serializable {
+
+  public static final String SESSION_REGION_NAME = RegionHelper.NAME + "_sessions";
+  public DistributedRule distributedRule = new DistributedRule();
+
+  public CacheRule cacheRule = new CacheRule();
+
+  public ClientCacheRule clientCacheRule = new ClientCacheRule();
+
+  @Rule
+  public transient RuleChain ruleChain = RuleChain.outerRule(distributedRule)
+      .around(cacheRule)
+      .around(clientCacheRule);
+
+
+  @Test
+  public void multipleGeodeServersCreateSessionRegion() {
+    final VM server0 = VM.getVM(0);
+    final VM server1 = VM.getVM(1);
+    final VM client = VM.getVM(2);
+
+    server0.invoke(this::startCacheServer);
+    server1.invoke(this::startCacheServer);
+
+    client.invoke(this::startClientSessionCache);
+
+    server0.invoke(this::validateServer);
+    server1.invoke(this::validateServer);
+  }
+
+  @Test
+  public void addServerToExistingClusterCreatesSessionRegion() {
+    final VM server0 = VM.getVM(0);
+    final VM server1 = VM.getVM(1);
+    final VM client = VM.getVM(2);
+
+    server0.invoke(this::startCacheServer);
+
+    client.invoke(this::startClientSessionCache);
+
+    server0.invoke(this::validateServer);
+
+    server1.invoke(this::startCacheServer);
+
+    // Session region may be created asynchronously on the second server
+    server1.invoke(() -> await().untilAsserted(this::validateServer));
+  }
+
+  @Test
+  public void startingAClientWithoutServersFails() {
+    final VM client = VM.getVM(2);
+
+    assertThatThrownBy(() -> client.invoke(this::startClientSessionCache))
+        .hasCauseInstanceOf(FunctionException.class);
+  }
+
+  @Test
+  public void canPrecreateSessionRegionBeforeStartingClient() {
+    final VM server0 = VM.getVM(0);
+    final VM server1 = VM.getVM(1);
+    final VM client = VM.getVM(2);
+
+    server0.invoke(this::startCacheServer);
+    server1.invoke(this::startCacheServer);
+
+    server0.invoke(this::createSessionRegion);
+    server1.invoke(this::createSessionRegion);
+
+    client.invoke(this::startClientSessionCache);
+
+    server0.invoke(this::validateServer);
+    server1.invoke(this::validateServer);
+  }
+
+  @Test
+  public void precreatedRegionIsNotCopiedToNewlyStartedServers() {
+    final VM server0 = VM.getVM(0);
+    final VM server1 = VM.getVM(1);
+    final VM client = VM.getVM(2);
+
+    server0.invoke(this::startCacheServer);
+
+
+    server0.invoke(this::createSessionRegion);
+
+
+    client.invoke(this::startClientSessionCache);
+    server1.invoke(this::startCacheServer);
+
+    server1.invoke(() -> await().untilAsserted(this::validateBootstrapped));
+
+    // server1 should not have created the session region
+    // If the user precreated the region, they must manually
+    // create it on all servers
+    server1.invoke(() -> {
+      Region<Object, Object> region = cacheRule.getCache().getRegion(SESSION_REGION_NAME);
+      assertThat(region).isNull();
+    });
+
+  }
+
+  @Test
+  public void cantPrecreateMismatchedSessionRegionBeforeStartingClient() {
+    final VM server0 = VM.getVM(0);
+    final VM server1 = VM.getVM(1);
+    final VM client = VM.getVM(2);
+
+    server0.invoke(this::startCacheServer);
+    server1.invoke(this::startCacheServer);
+
+    server0.invoke(this::createMismatchedSessionRegion);
+    server1.invoke(this::createMismatchedSessionRegion);
+
+    assertThatThrownBy(() -> client.invoke(this::startClientSessionCache))
+        .hasCauseInstanceOf(IllegalStateException.class);
+  }
+
+  private void createSessionRegion() {
+    Cache cache = cacheRule.getCache();
+
+    Region region =
+        cache.<String, HttpSession>createRegionFactory(RegionShortcut.PARTITION_REDUNDANT)
+            .setCustomEntryIdleTimeout(new SessionCustomExpiry())
+            .create(SESSION_REGION_NAME);
+  }
+
+  private void createMismatchedSessionRegion() {
+    Cache cache = cacheRule.getCache();
+
+    Region region = cache.<String, HttpSession>createRegionFactory(RegionShortcut.PARTITION)
+        .setCustomEntryIdleTimeout(new SessionCustomExpiry())
+        .create(SESSION_REGION_NAME);
+  }
+
+  private void validateSessionRegion() {
+    final InternalCache cache = cacheRule.getCache();
+
+    final Region region = cache.getRegion(SESSION_REGION_NAME);
+    assertThat(region).isNotNull();
+
+    final RegionAttributes<Object, Object> expectedAttributes =
+        cache.getRegionAttributes(RegionShortcut.PARTITION_REDUNDANT.toString());
+
+    final RegionAttributes attributes = region.getAttributes();
+    assertThat(attributes.getScope()).isEqualTo(expectedAttributes.getScope());
+    assertThat(attributes.getDataPolicy()).isEqualTo(expectedAttributes.getDataPolicy());
+    assertThat(attributes.getPartitionAttributes())
+        .isEqualTo(expectedAttributes.getPartitionAttributes());
+    assertThat(attributes.getCustomEntryIdleTimeout()).isInstanceOf(SessionCustomExpiry.class);
+  }
+
+  private void validateServer() {
+    validateBootstrapped();
+    validateSessionRegion();
+  }
+
+  private void validateBootstrapped() {
+    final InternalCache cache = cacheRule.getCache();
+
+    final DistributionManager distributionManager =
+        cache.getInternalDistributedSystem().getDistributionManager();
+    final Collection<MembershipListener> listeners =
+        distributionManager.getMembershipListeners();
+    assertThat(listeners)
+        .filteredOn(listener -> listener instanceof BootstrappingFunction)
+        .hasSize(1);
+    assertThat(FunctionService.getFunction(CreateRegionFunction.ID))
+        .isInstanceOf(CreateRegionFunction.class);
+    assertThat(FunctionService.getFunction(TouchPartitionedRegionEntriesFunction.ID))
+        .isInstanceOf(TouchPartitionedRegionEntriesFunction.class);
+    assertThat(FunctionService.getFunction(TouchReplicatedRegionEntriesFunction.ID))
+        .isInstanceOf(TouchReplicatedRegionEntriesFunction.class);
+    assertThat(FunctionService.getFunction(RegionSizeFunction.ID))
+        .isInstanceOf(RegionSizeFunction.class);
+
+    final Region<String, RegionConfiguration> region =
+        cache.getRegion(CreateRegionFunction.REGION_CONFIGURATION_METADATA_REGION);
+    assertThat(region).isNotNull();
+
+    final RegionAttributes<String, RegionConfiguration> attributes = region.getAttributes();
+    assertThat(attributes.getDataPolicy()).isEqualTo(DataPolicy.REPLICATE);
+    assertThat(attributes.getScope()).isEqualTo(Scope.DISTRIBUTED_ACK);
+    assertThat(attributes.getDataPolicy()).isEqualTo(DataPolicy.REPLICATE);
+    assertThat(attributes.getCacheListeners())
+        .filteredOn(listener -> listener instanceof RegionConfigurationCacheListener)
+        .hasSize(1);
+  }
+
+  private void startClientSessionCache() {
+    final SessionManager sessionManager = mock(SessionManager.class);
+    final Log logger = mock(Log.class);
+    when(sessionManager.getLogger()).thenReturn(logger);
+    when(sessionManager.getRegionName()).thenReturn(RegionHelper.NAME + "_sessions");
+    when(sessionManager.getRegionAttributesId())
+        .thenReturn(RegionShortcut.PARTITION_REDUNDANT.toString());
+
+    final ClientCacheFactory clientCacheFactory = new ClientCacheFactory();
+    clientCacheFactory.addPoolLocator("localhost", DistributedTestUtils.getLocatorPort());
+    clientCacheFactory.setPoolSubscriptionEnabled(true);
+    clientCacheRule.createClientCache(clientCacheFactory);
+
+    final ClientCache clientCache = clientCacheRule.getClientCache();
+    new ClientServerSessionCache(sessionManager, clientCache).initialize();
+  }
+
+  private void startCacheServer() throws IOException {
+    final Cache cache = cacheRule.getOrCreateCache();
+    final CacheServer cacheServer = cache.addCacheServer();
+    cacheServer.setPort(0);
+    cacheServer.start();
+  }
+}
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/CreateRegionFunction.java
b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/CreateRegionFunction.java
index 73288ff..fbb2e53 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/CreateRegionFunction.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/CreateRegionFunction.java
@@ -29,6 +29,7 @@ import org.apache.geode.InternalGemFireError;
 import org.apache.geode.cache.AttributesFactory;
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.DataPolicy;
 import org.apache.geode.cache.Declarable;
 import org.apache.geode.cache.Region;
 import org.apache.geode.cache.RegionAttributes;
@@ -58,7 +59,7 @@ public class CreateRegionFunction implements Function, Declarable, DataSerializa
   private static final boolean DUMP_SESSION_CACHE_XML =
       Boolean.getBoolean("gemfiremodules.dumpSessionCacheXml");
 
-  private static final String REGION_CONFIGURATION_METADATA_REGION =
+  static final String REGION_CONFIGURATION_METADATA_REGION =
       "__regionConfigurationMetadata";
 
   public CreateRegionFunction() {
@@ -219,7 +220,8 @@ public class CreateRegionFunction implements Function, Declarable, DataSerializa
     GemFireCacheImpl gemFireCache = (GemFireCacheImpl) cache;
     InternalRegionArguments ira = new InternalRegionArguments().setInternalRegion(true);
     AttributesFactory af = new AttributesFactory();
-    af.setScope(Scope.LOCAL);
+    af.setDataPolicy(DataPolicy.REPLICATE);
+    af.setScope(Scope.DISTRIBUTED_ACK);
     af.addCacheListener(new RegionConfigurationCacheListener());
     RegionAttributes ra = af.create();
     try {
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/SessionCustomExpiry.java
b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/SessionCustomExpiry.java
index ffb7cf2..046b5a4 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/SessionCustomExpiry.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/SessionCustomExpiry.java
@@ -68,4 +68,8 @@ public class SessionCustomExpiry
     return getClass().hashCode();
   }
 
+  @Override
+  public String toString() {
+    return this.getClass().toString();
+  }
 }
diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/session/tests/ContainerManager.java
b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/session/tests/ContainerManager.java
index 6bbf8f3..92ff515 100644
--- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/session/tests/ContainerManager.java
+++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/session/tests/ContainerManager.java
@@ -212,6 +212,13 @@ public class ContainerManager {
   }
 
   /**
+   * Remove the given container
+   */
+  public boolean removeContainer(ServerContainer container) {
+    return containers.remove(container);
+  }
+
+  /**
    * Get the indexes of all active containers
    */
   private ArrayList<Integer> getActiveContainerIndexes() {
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/TomcatClientServerTest.java
b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/TomcatClientServerTest.java
index 1e978da..8b06794 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/TomcatClientServerTest.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/session/tests/TomcatClientServerTest.java
@@ -32,7 +32,8 @@ import org.apache.geode.test.junit.rules.GfshCommandRule;
  * Sets up the server needed for the client container to connect to
  */
 public abstract class TomcatClientServerTest extends CargoTestBase {
-  private String serverName;
+  private String serverName1;
+  private String serverName2;
 
   @Rule
   public transient TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -46,12 +47,18 @@ public abstract class TomcatClientServerTest extends CargoTestBase {
    */
   @Before
   public void startServer() throws Exception {
+    serverName1 = startAServer(1);
+    serverName2 = startAServer(2);
+  }
+
+  private String startAServer(int serverNumber) {
     // List of all the jars for tomcat to put on the server classpath
     String libDirJars = install.getHome() + "/lib/*";
     String binDirJars = install.getHome() + "/bin/*";
 
     // Set server name based on the test about to be run
-    serverName = getClass().getSimpleName().concat("_").concat(testName.getMethodName());
+    String serverName =
+        getClass().getSimpleName() + "_" + testName.getMethodName() + "_" + serverNumber;
 
     // Create command string for starting server
     CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
@@ -66,6 +73,8 @@ public abstract class TomcatClientServerTest extends CargoTestBase {
 
     // Start server
     gfsh.executeAndAssertThat(command.toString()).statusIsSuccess();
+
+    return serverName;
   }
 
   /**
@@ -73,6 +82,11 @@ public abstract class TomcatClientServerTest extends CargoTestBase {
    */
   @After
   public void stopServer() throws Exception {
+    stopAServer(serverName1);
+    stopAServer(serverName2);
+  }
+
+  private void stopAServer(String serverName) {
     CommandStringBuilder command = new CommandStringBuilder(CliStrings.STOP_SERVER);
     command.addOption(CliStrings.STOP_SERVER__DIR, serverName);
     gfsh.executeAndAssertThat(command.toString()).statusIsSuccess();
diff --git a/geode-assembly/src/upgradeTest/java/org/apache/geode/session/tests/Tomcat8ClientServerRollingUpgradeTest.java
b/geode-assembly/src/upgradeTest/java/org/apache/geode/session/tests/Tomcat8ClientServerRollingUpgradeTest.java
new file mode 100644
index 0000000..e00d4cc
--- /dev/null
+++ b/geode-assembly/src/upgradeTest/java/org/apache/geode/session/tests/Tomcat8ClientServerRollingUpgradeTest.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version
2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain
a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express
+ * or implied. See the License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.apache.geode.session.tests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.lang3.JavaVersion;
+import org.apache.commons.lang3.SystemUtils;
+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.TemporaryFolder;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.internal.UniquePortSupplier;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.junit.categories.BackwardCompatibilityTest;
+import org.apache.geode.test.junit.rules.gfsh.GfshRule;
+import org.apache.geode.test.junit.rules.gfsh.GfshScript;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+import org.apache.geode.test.version.VersionManager;
+
+/**
+ * This test iterates through the versions of Geode and executes session client compatibility
with
+ * the current version of Geode.
+ */
+@Category({BackwardCompatibilityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class Tomcat8ClientServerRollingUpgradeTest {
+  private final UniquePortSupplier portSupplier = new UniquePortSupplier();
+  private final String oldVersion;
+  private String locatorDir;
+  private String server1Dir;
+  private String server2Dir;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Collection<String> data() {
+    List<String> result = VersionManager.getInstance().getVersionsWithoutCurrent();
+    int minimumVersion = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9) ? 180 : 170;
+    result.removeIf(s -> Integer.parseInt(s) < minimumVersion);
+    return result;
+  }
+
+  @Rule
+  public transient GfshRule oldGfsh;
+
+  @Rule
+  public final transient GfshRule currentGfsh = new GfshRule();
+
+  @Rule
+  public TemporaryFolder tempFolder = new TemporaryFolder();
+
+  @Rule
+  public transient TestName testName = new TestName();
+
+  protected transient Client client;
+  protected transient ContainerManager manager;
+
+
+  protected TomcatInstall tomcat8AndOldModules;
+  protected TomcatInstall tomcat8AndCurrentModules;
+
+  protected int locatorPort;
+  protected int locatorJmxPort;
+
+  protected String classPathTomcat8AndCurrentModules;
+  private String classPathTomcat8AndOldModules;
+
+  public Tomcat8ClientServerRollingUpgradeTest(String version) {
+    this.oldVersion = version;
+    oldGfsh = new GfshRule(oldVersion);
+  }
+
+  protected void startServer(String name, String classPath, int locatorPort, GfshRule gfsh,
+      String serverDir) throws Exception {
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.START_SERVER);
+    command.addOption(CliStrings.START_SERVER__NAME, name);
+    command.addOption(CliStrings.START_SERVER__SERVER_PORT, "0");
+    command.addOption(CliStrings.START_SERVER__CLASSPATH, classPath);
+    command.addOption(CliStrings.START_SERVER__LOCATORS, "localhost[" + locatorPort + "]");
+    command.addOption(CliStrings.START_SERVER__DIR, serverDir);
+    gfsh.execute(GfshScript.of(command.toString()).expectExitCode(0));
+  }
+
+  protected void startLocator(String name, String classPath, int port, GfshRule gfsh,
+      String locatorDir) throws Exception {
+    CommandStringBuilder locStarter = new CommandStringBuilder(CliStrings.START_LOCATOR);
+    locStarter.addOption(CliStrings.START_LOCATOR__MEMBER_NAME, name);
+    locStarter.addOption(CliStrings.START_LOCATOR__CLASSPATH, classPath);
+    locStarter.addOption(CliStrings.START_LOCATOR__PORT, Integer.toString(port));
+    locStarter.addOption(CliStrings.START_LOCATOR__DIR, locatorDir);
+    locStarter.addOption(CliStrings.START_LOCATOR__HTTP_SERVICE_PORT, "0");
+    locStarter.addOption(CliStrings.START_LOCATOR__J,
+        "-Dgemfire.jmx-manager-port=" + locatorJmxPort);
+    gfsh.execute(GfshScript.of(locStarter.toString()).expectExitCode(0));
+  }
+
+  @Before
+  public void setup() throws Exception {
+    VersionManager versionManager = VersionManager.getInstance();
+    String installLocation = versionManager.getInstall(oldVersion);
+    File oldBuild = new File(installLocation);
+    File oldModules = new File(installLocation + "/tools/Modules/");
+
+
+    tomcat8AndOldModules =
+        new TomcatInstall("Tomcat8AndOldModules", TomcatInstall.TomcatVersion.TOMCAT8,
+            ContainerInstall.ConnectionType.CLIENT_SERVER,
+            oldModules.getAbsolutePath(),
+            oldBuild.getAbsolutePath() + "/lib",
+            portSupplier::getAvailablePort);
+
+    tomcat8AndCurrentModules =
+        new TomcatInstall("Tomcat8AndCurrentModules", TomcatInstall.TomcatVersion.TOMCAT8,
+            ContainerInstall.ConnectionType.CLIENT_SERVER,
+            portSupplier::getAvailablePort);
+
+    classPathTomcat8AndOldModules = tomcat8AndOldModules.getHome() + "/lib/*" + File.pathSeparator
+        + tomcat8AndOldModules.getHome() + "/bin/*";
+
+    classPathTomcat8AndCurrentModules =
+        tomcat8AndCurrentModules.getHome() + "/lib/*" + File.pathSeparator
+            + tomcat8AndCurrentModules.getHome() + "/bin/*";
+
+    // Get available port for the locator
+    locatorPort = portSupplier.getAvailablePort();
+    locatorJmxPort = portSupplier.getAvailablePort();
+
+    tomcat8AndOldModules.setDefaultLocatorPort(locatorPort);
+    tomcat8AndCurrentModules.setDefaultLocatorPort(locatorPort);
+
+    client = new Client();
+    manager = new ContainerManager();
+    // Due to parameterization of the test name, the URI would be malformed. Instead, it
strips off
+    // the [] symbols
+    manager.setTestName(testName.getMethodName().replace("[", "").replace("]", ""));
+
+    locatorDir = tempFolder.newFolder("loc").getPath();
+    server1Dir = tempFolder.newFolder("server1").getPath();
+    server2Dir = tempFolder.newFolder("server2").getPath();
+  }
+
+  /**
+   * Stops all containers that were previously started and cleans up their configurations
+   */
+  @After
+  public void stop() throws Exception {
+    manager.stopAllActiveContainers();
+    manager.cleanUp();
+
+    CommandStringBuilder connect = new CommandStringBuilder(CliStrings.CONNECT)
+        .addOption(CliStrings.CONNECT__LOCATOR, "localhost[" + locatorPort + "]");
+
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.SHUTDOWN);
+    command.addOption(CliStrings.INCLUDE_LOCATORS, "true");
+    final GfshScript script = GfshScript.of(connect.toString(), command.toString());
+    try {
+      oldGfsh.execute(script);
+    } catch (Throwable e) {
+      // ignore
+    }
+
+    try {
+      currentGfsh.execute(script);
+    } catch (Throwable e) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void canDoARollingUpgradeOfGeodeServersWithSessionModules() throws Exception {
+
+    startLocator("loc", classPathTomcat8AndOldModules, locatorPort, oldGfsh, locatorDir);
+    startServer("server1", classPathTomcat8AndOldModules, locatorPort, oldGfsh, server1Dir);
+    startServer("server2", classPathTomcat8AndOldModules, locatorPort, oldGfsh, server2Dir);
+    createRegion(oldGfsh);
+
+    // Start two tomcat servers with the old geode modules
+    ServerContainer container1 = manager.addContainer(tomcat8AndOldModules);
+    ServerContainer container2 = manager.addContainer(tomcat8AndOldModules);
+
+    // This has to happen at the start of every test
+    manager.startAllInactiveContainers();
+
+    verifySessionReplication();
+
+    // Upgrade the geode locator
+    stopLocator(oldGfsh, locatorDir);
+    startLocator("loc", classPathTomcat8AndCurrentModules, locatorPort, currentGfsh, locatorDir);
+
+    // Upgrade a server
+    stopServer(oldGfsh, server1Dir);
+    startServer("server1", classPathTomcat8AndCurrentModules, locatorPort, currentGfsh, server1Dir);
+
+    // verify again
+    verifySessionReplication();
+
+    // Upgrade second server
+    stopServer(oldGfsh, server2Dir);
+    startServer("server2", classPathTomcat8AndCurrentModules, locatorPort, currentGfsh, server2Dir);
+
+    // verify again
+    verifySessionReplication();
+
+    // Upgrade a tomcat server
+    container1.stop();
+    manager.removeContainer(container1);
+    ServerContainer newContainer1 = manager.addContainer(tomcat8AndCurrentModules);
+    newContainer1.start();
+
+    verifySessionReplication();
+
+    // Upgrade the second tomcat server
+    container2.stop();
+    manager.removeContainer(container2);
+    ServerContainer newContainer2 = manager.addContainer(tomcat8AndCurrentModules);
+    newContainer2.start();
+
+    verifySessionReplication();
+  }
+
+  private void createRegion(GfshRule gfsh) {
+    CommandStringBuilder connect = new CommandStringBuilder(CliStrings.CONNECT)
+        .addOption(CliStrings.CONNECT__LOCATOR, "localhost[" + locatorPort + "]");
+
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.CREATE_REGION);
+    command.addOption(CliStrings.CREATE_REGION__REGIONSHORTCUT,
+        RegionShortcut.PARTITION_REDUNDANT.name())
+        .addOption(CliStrings.CREATE_REGION__REGION, "gemfire_modules_sessions")
+        .addOption(CliStrings.CREATE_REGION__STATISTICSENABLED, "true")
+        .addOption(CliStrings.ENTRY_IDLE_TIME_CUSTOM_EXPIRY,
+            "org.apache.geode.modules.util.SessionCustomExpiry");
+
+    final GfshScript script = GfshScript.of(connect.toString(), command.toString());
+    gfsh.execute(script);
+  }
+
+  private void stopLocator(GfshRule gfsh, String locatorDir) {
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.STOP_LOCATOR)
+        .addOption(CliStrings.STOP_LOCATOR__DIR, locatorDir);
+    gfsh.execute(command.toString());
+  }
+
+  private void stopServer(GfshRule gfsh, String serverDir) {
+    CommandStringBuilder command = new CommandStringBuilder(CliStrings.STOP_SERVER)
+        .addOption(CliStrings.STOP_SERVER__DIR, serverDir);
+    gfsh.execute(command.toString());
+  }
+
+  private void verifySessionReplication() throws IOException, URISyntaxException {
+    String key = "value_testSessionPersists";
+    String value = "Foo" + System.currentTimeMillis();
+
+    client.setPort(Integer.parseInt(manager.getContainerPort(0)));
+    Client.Response resp = client.set(key, value);
+    String cookie = resp.getSessionCookie();
+
+    for (int i = 0; i < manager.numContainers(); i++) {
+      System.out.println("Checking get for container:" + i);
+      client.setPort(Integer.parseInt(manager.getContainerPort(i)));
+      resp = client.get(key);
+
+      assertEquals("Sessions are not replicating properly", cookie, resp.getSessionCookie());
+      assertEquals("Session data is not replicating properly", value, resp.getResponse());
+    }
+  }
+
+}
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterDistributionManager.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterDistributionManager.java
index c523164..470b0d1 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterDistributionManager.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterDistributionManager.java
@@ -1946,6 +1946,11 @@ public class ClusterDistributionManager implements DistributionManager
{
     this.membershipListeners.remove(l);
   }
 
+  @Override
+  public Collection<MembershipListener> getMembershipListeners() {
+    return Collections.unmodifiableSet(this.membershipListeners.keySet());
+  }
+
   /**
    * Adds a <code>MembershipListener</code> to this distribution manager.
    */
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionManager.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionManager.java
index 09eaabc..44ac5c7 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionManager.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionManager.java
@@ -199,6 +199,8 @@ public interface DistributionManager extends ReplySender {
    */
   void removeMembershipListener(MembershipListener l);
 
+  Collection<MembershipListener> getMembershipListeners();
+
   /**
    * Removes a <code>MembershipListener</code> listening for all members from
this distribution
    * manager.
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/LonerDistributionManager.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/LonerDistributionManager.java
index 16c61a0..56c00df 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/LonerDistributionManager.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/LonerDistributionManager.java
@@ -255,6 +255,11 @@ public class LonerDistributionManager implements DistributionManager
{
 
   public void removeAllMembershipListener(MembershipListener l) {}
 
+  @Override
+  public Collection<MembershipListener> getMembershipListeners() {
+    return Collections.emptySet();
+  }
+
   public void addAdminConsole(InternalDistributedMember p_id) {}
 
   public DMStats getStats() {
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/RegionAttributesCreation.java
b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/RegionAttributesCreation.java
index 3eec93e..74eab43 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/RegionAttributesCreation.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/RegionAttributesCreation.java
@@ -387,8 +387,9 @@ public class RegionAttributesCreation extends UserSpecifiedRegionAttributes
           "EntryIdleTimeout is not the same");
     }
     if (!equal(this.customEntryIdleTimeout, other.getCustomEntryIdleTimeout())) {
-      throw new RuntimeException(
-          "CustomEntryIdleTimeout is not the same");
+      throw new RuntimeException(String.format(
+          "CustomEntryIdleTimeout is not the same. this %s, other: %s", this.customEntryIdleTimeout,
+          other.getCustomEntryIdleTimeout()));
     }
     if (!equal(this.entryTimeToLive, other.getEntryTimeToLive())) {
       throw new RuntimeException(
@@ -401,7 +402,7 @@ public class RegionAttributesCreation extends UserSpecifiedRegionAttributes
     if (!equal(this.partitionAttributes, other.getPartitionAttributes())) {
       throw new RuntimeException(
           String.format("PartitionAttributes are not the same. this: %s, other: %s",
-              new Object[] {this, other.getPartitionAttributes()}));
+              this, other.getPartitionAttributes()));
     }
     if (!equal(this.membershipAttributes, other.getMembershipAttributes())) {
       throw new RuntimeException(
@@ -414,8 +415,7 @@ public class RegionAttributesCreation extends UserSpecifiedRegionAttributes
     if (!equal(this.evictionAttributes, other.getEvictionAttributes())) {
       throw new RuntimeException(
           String.format("Eviction Attributes are not the same: this: %s other: %s",
-
-              new Object[] {this.evictionAttributes, other.getEvictionAttributes()}));
+              this.evictionAttributes, other.getEvictionAttributes()));
     }
     if (this.diskStoreName == null) {
       // only compare the DWA, diskDirs and diskSizes when disk store is not configured


Mime
View raw message