You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ji...@apache.org on 2018/06/19 17:36:44 UTC
[geode] branch develop updated: GEODE-5284: Add testing surrounding
MBean persistence during member failure and reconnection. (#2066)
This is an automated email from the ASF dual-hosted git repository.
jinmeiliao 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 82bc8f9 GEODE-5284: Add testing surrounding MBean persistence during member failure and reconnection. (#2066)
82bc8f9 is described below
commit 82bc8f9cb82806b6e5b5e3a91c382d8372eade00
Author: Helena Bales <hb...@pivotal.io>
AuthorDate: Tue Jun 19 10:36:40 2018 -0700
GEODE-5284: Add testing surrounding MBean persistence during member failure and reconnection. (#2066)
* Add null-check to ConfigurationChangeListener to avoid NPE when a locator joins.
* Add withPort method to MemberStarterRule to specify membership port
* Add testing associated with MemberStarterRule addition
* Ensure cleanup of members in MemberStarterRuleIntegrationTest.
* Improve names and extract helper methods
* use the methods provided by the starter rules to force disconnect and
to reconnect servers and locators. Adds a parameter in InternalLocator
to track the status of the reconnect. Adds export of cache xml for
reconnect so that the server is able to restore its state following
disconnect.
Co-Authored-by: Helena Bales <hb...@pivotal.io>
Co-Authored-by: Dale Emery <da...@dhemery.com>
Co-Authored-by: Patrick Rhomberg <pr...@pivotal.io>
Co-Authored-By: Jinmei Liao <ji...@pivotal.io>
---
.../distributed/internal/InternalLocator.java | 7 +
.../callbacks/ConfigurationChangeListener.java | 12 +-
.../internal/security/MBeanServerWrapper.java | 1 -
.../membership/gms/MembershipManagerHelper.java | 3 +-
.../management/JMXMBeanReconnectDUnitTest.java | 305 +++++++++++++++++++++
.../apache/geode/test/dunit/rules/MemberVM.java | 27 ++
.../tests/MemberStarterRuleIntegrationTest.java | 77 ++++++
.../dunit/rules/tests/MemberStarterRuleTest.java | 9 +
.../geode/test/junit/rules/LocatorStarterRule.java | 5 +-
.../geode/test/junit/rules/MemberStarterRule.java | 7 +-
.../geode/test/junit/rules/ServerStarterRule.java | 4 +-
.../apache/geode/test/junit/rules/VMProvider.java | 2 +-
12 files changed, 449 insertions(+), 10 deletions(-)
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
index 8b78b80..2ed7b9a 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
@@ -166,6 +166,8 @@ public class InternalLocator extends Locator implements ConnectListener {
*/
private volatile boolean stoppedForReconnect;
+ private volatile boolean reconnected;
+
/**
* whether the locator was stopped during forced-disconnect processing
*/
@@ -936,6 +938,7 @@ public class InternalLocator extends Locator implements ConnectListener {
if (!restarted) {
stoppedForReconnect = false;
}
+ reconnected = restarted;
}
InternalLocator.this.restartThread = null;
}
@@ -944,6 +947,10 @@ public class InternalLocator extends Locator implements ConnectListener {
this.restartThread.start();
}
+ public boolean isReconnected() {
+ return reconnected;
+ }
+
/**
* reconnects the locator to a restarting DistributedSystem. If quorum checks are enabled this
* will start peer location services before a distributed system is available if the quorum check
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/callbacks/ConfigurationChangeListener.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/callbacks/ConfigurationChangeListener.java
index 71d7005..64e9724 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/callbacks/ConfigurationChangeListener.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/callbacks/ConfigurationChangeListener.java
@@ -17,7 +17,6 @@ package org.apache.geode.management.internal.configuration.callbacks;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
-import java.util.Optional;
import java.util.Set;
import org.apache.commons.io.FileUtils;
@@ -92,6 +91,10 @@ public class ConfigurationChangeListener extends CacheListenerAdapter<String, Co
}
String triggerMemberId = (String) event.getCallbackArgument();
+ if (triggerMemberId == null || newJars.isEmpty()) {
+ return;
+ }
+
DistributedMember locator = getDistributedMember(triggerMemberId);
for (String jarAdded : newJars) {
try {
@@ -106,8 +109,9 @@ public class ConfigurationChangeListener extends CacheListenerAdapter<String, Co
Set<DistributedMember> locators = new HashSet<>(
cache.getDistributionManager().getAllHostedLocatorsWithSharedConfiguration().keySet());
- Optional<DistributedMember> locator =
- locators.stream().filter(x -> x.getId().equals(memberName)).findFirst();
- return locator.get();
+ return locators.stream()
+ .filter(x -> x.getId().equals(memberName))
+ .findFirst()
+ .orElse(null);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/security/MBeanServerWrapper.java b/geode-core/src/main/java/org/apache/geode/management/internal/security/MBeanServerWrapper.java
index 549e89f..24f0dbe 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/security/MBeanServerWrapper.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/security/MBeanServerWrapper.java
@@ -61,7 +61,6 @@ import org.apache.geode.security.ResourcePermission.Target;
*/
public class MBeanServerWrapper implements MBeanServerForwarder {
- // TODO: make volatile or verify this is thread-safe
private MBeanServer mbs;
private final SecurityService securityService;
diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/MembershipManagerHelper.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/MembershipManagerHelper.java
index f08c05d..09169f2 100644
--- a/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/MembershipManagerHelper.java
+++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/MembershipManagerHelper.java
@@ -124,10 +124,11 @@ public class MembershipManagerHelper {
public static void crashDistributedSystem(final DistributedSystem msys) {
msys.getLogWriter().info("crashing distributed system: " + msys);
+ GMSMembershipManager mgr = ((GMSMembershipManager) getMembershipManager(msys));
+ mgr.saveCacheXmlForReconnect(false);
MembershipManagerHelper.inhibitForcedDisconnectLogging(true);
MembershipManagerHelper.beSickMember(msys);
MembershipManagerHelper.playDead(msys);
- GMSMembershipManager mgr = ((GMSMembershipManager) getMembershipManager(msys));
mgr.forceDisconnect("for testing");
// wait at most 10 seconds for system to be disconnected
Awaitility.await().pollInterval(1, TimeUnit.SECONDS).until(() -> !msys.isConnected());
diff --git a/geode-core/src/test/java/org/apache/geode/management/JMXMBeanReconnectDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/JMXMBeanReconnectDUnitTest.java
new file mode 100644
index 0000000..2bf49c7
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/JMXMBeanReconnectDUnitTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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.management;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.stream.Collectors.toList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+import static org.awaitility.Awaitility.waitAtMost;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.management.internal.SystemManagementService;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.JMXTest;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+import org.apache.geode.test.junit.rules.MBeanServerConnectionRule;
+
+@Category({DistributedTest.class, JMXTest.class})
+public class JMXMBeanReconnectDUnitTest {
+ private static final String LOCATOR_1_NAME = "locator-one";
+ private static final String LOCATOR_2_NAME = "locator-two";
+ private static final String REGION_PATH = "/test-region-1";
+ private static final int LOCATOR_1_VM_INDEX = 0;
+ private static final int LOCATOR_2_VM_INDEX = 1;
+ private static final int LOCATOR_COUNT = 2;
+ private static final int SERVER_1_VM_INDEX = 2;
+ private static final int SERVER_2_VM_INDEX = 3;
+ private static final int SERVER_COUNT = 2;
+
+ private int locator1JmxPort, locator2JmxPort;
+
+ private MemberVM locator1, locator2, server1, server2;
+
+ @Rule
+ public ClusterStartupRule lsRule = new ClusterStartupRule();
+
+ @Rule
+ public GfshCommandRule gfsh = new GfshCommandRule();
+
+ @Rule
+ public MBeanServerConnectionRule jmxConnectionRule = new MBeanServerConnectionRule();
+
+ @Before
+ public void before() throws Exception {
+ int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(LOCATOR_COUNT);
+ locator1JmxPort = ports[0];
+ locator2JmxPort = ports[1];
+
+ locator1 = lsRule.startLocatorVM(LOCATOR_1_VM_INDEX, locator1Properties());
+ locator2 = lsRule.startLocatorVM(LOCATOR_2_VM_INDEX, locator2Properties(), locator1.getPort());
+
+ server1 = lsRule.startServerVM(SERVER_1_VM_INDEX, locator1.getPort());
+ server2 = lsRule.startServerVM(SERVER_2_VM_INDEX, locator1.getPort());
+
+ gfsh.connectAndVerify(locator1);
+ gfsh.executeAndAssertThat(
+ "create region --type=REPLICATE --name=" + REGION_PATH + " --enable-statistics=true")
+ .statusIsSuccess();
+
+ locator1.waitTillRegionsAreReadyOnServers(REGION_PATH, SERVER_COUNT);
+ waitForLocatorsToAgreeOnMembership();
+ }
+
+ @Test
+ public void testLocalBeans_MaintainServerAndCrashLocator() {
+ List<String> initialServerBeans = canonicalBeanNamesFor(server1);
+
+ locator1.forceDisconnectMember();
+
+ List<String> intermediateServerBeans = canonicalBeanNamesFor(server1);
+
+ assertThat(intermediateServerBeans)
+ .containsExactlyElementsOf(initialServerBeans);
+
+ locator1.waitTilLocatorFullyReconnected();
+
+ List<String> finalServerBeans = canonicalBeanNamesFor(server1);
+
+ assertThat(finalServerBeans)
+ .containsExactlyElementsOf(initialServerBeans);
+ }
+
+ @Test
+ public void testLocalBeans_MaintainLocatorAndCrashServer() {
+ List<String> initialLocatorBeans = canonicalBeanNamesFor(locator1);
+
+ server1.forceDisconnectMember();
+
+ List<String> intermediateLocatorBeans = canonicalBeanNamesFor(locator1);
+
+ assertThat(intermediateLocatorBeans)
+ .containsExactlyElementsOf(initialLocatorBeans);
+
+ server1.waitTilServerFullyReconnected();
+ locator1.waitTillRegionsAreReadyOnServers(REGION_PATH, SERVER_COUNT);
+
+ List<String> finalLocatorBeans = canonicalBeanNamesFor(locator1);
+
+ assertThat(finalLocatorBeans)
+ .containsExactlyElementsOf(initialLocatorBeans);
+ }
+
+ @Test
+ public void testRemoteBeanKnowledge_MaintainServerAndCrashLocator() throws IOException {
+ List<ObjectName> initialLocator1GemfireBeans =
+ getFederatedGemfireBeansFrom(locator1);
+ List<ObjectName> initialLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ assertThat(initialLocator1GemfireBeans)
+ .containsExactlyElementsOf(initialLocator2GemfireBeans);
+
+ locator1.forceDisconnectMember();
+
+ List<ObjectName> intermediateLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ List<ObjectName> locatorBeansExcludingStoppedMember = initialLocator2GemfireBeans.stream()
+ .filter(excludingBeansFor(LOCATOR_1_NAME)).collect(toList());
+
+ assertThat(intermediateLocator2GemfireBeans)
+ .containsExactlyElementsOf(locatorBeansExcludingStoppedMember);
+
+ locator1.waitTilLocatorFullyReconnected();
+ waitForLocatorsToAgreeOnMembership();
+
+ List<ObjectName> finalLocator1GemfireBeans =
+ getFederatedGemfireBeansFrom(locator1);
+ List<ObjectName> finalLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ assertSoftly(softly -> {
+ softly.assertThat(finalLocator1GemfireBeans)
+ .containsExactlyElementsOf(finalLocator2GemfireBeans);
+ softly.assertThat(finalLocator1GemfireBeans)
+ .containsExactlyElementsOf(initialLocator2GemfireBeans);
+ });
+ }
+
+ @Test
+ public void testRemoteBeanKnowledge_MaintainLocatorAndCrashServer()
+ throws IOException {
+ List<ObjectName> initialLocator1GemfireBeans =
+ getFederatedGemfireBeansFrom(locator1);
+ List<ObjectName> initialLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ assertThat(initialLocator1GemfireBeans)
+ .containsExactlyElementsOf(initialLocator2GemfireBeans);
+
+ server1.forceDisconnectMember();
+
+ List<ObjectName> intermediateLocator1GemfireBeans =
+ getFederatedGemfireBeansFrom(locator1);
+ List<ObjectName> intermediateLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ List<ObjectName> locatorBeansExcludingStoppedMember = initialLocator1GemfireBeans.stream()
+ .filter(excludingBeansFor("server-" + SERVER_1_VM_INDEX))
+ .collect(toList());
+
+ assertSoftly(softly -> {
+ softly.assertThat(intermediateLocator2GemfireBeans)
+ .containsExactlyElementsOf(intermediateLocator1GemfireBeans);
+ softly.assertThat(intermediateLocator2GemfireBeans)
+ .containsExactlyElementsOf(locatorBeansExcludingStoppedMember);
+ });
+
+ server1.waitTilServerFullyReconnected();
+ locator1.waitTillRegionsAreReadyOnServers(REGION_PATH, SERVER_COUNT);
+ waitForLocatorsToAgreeOnMembership();
+
+ List<ObjectName> finalLocator1GemfireBeans =
+ getFederatedGemfireBeansFrom(locator1);
+ List<ObjectName> finalLocator2GemfireBeans =
+ getFederatedGemfireBeansFrom(locator2);
+
+ assertSoftly(softly -> {
+ softly.assertThat(finalLocator1GemfireBeans)
+ .containsExactlyElementsOf(finalLocator2GemfireBeans);
+ softly.assertThat(finalLocator1GemfireBeans)
+ .containsExactlyElementsOf(initialLocator2GemfireBeans);
+ });
+ }
+
+ private static List<ObjectName> getFederatedGemfireBeansFrom(MemberVM member)
+ throws IOException {
+ String url = jmxBeanLocalhostUrlString(member.getJmxPort());
+ MBeanServerConnection remoteMBS = connectToMBeanServer(url);
+ return getFederatedGemfireBeanObjectNames(remoteMBS);
+ }
+
+ private static MBeanServerConnection connectToMBeanServer(String url) throws IOException {
+ final JMXServiceURL serviceURL = new JMXServiceURL(url);
+ JMXConnector conn = JMXConnectorFactory.connect(serviceURL);
+ return conn.getMBeanServerConnection();
+ }
+
+ private static List<ObjectName> getFederatedGemfireBeanObjectNames(
+ MBeanServerConnection remoteMBS)
+ throws IOException {
+ Set<ObjectName> allBeans = remoteMBS.queryNames(null, null);
+ // Each locator will have a "Manager" bean that is a part of the above query,
+ // representing the ManagementAdapter.
+ // This bean is registered (and so included in its own queries),
+ // but *not* federated (and so is not included in another locator's bean queries).
+ // For the scope of this test, we do not consider these "service=Manager" beans.
+ return allBeans.stream()
+ .filter(b -> b.toString().contains("GemFire"))
+ .filter(b -> !b.toString().contains("service=Manager,type=Member,member=locator"))
+ .sorted()
+ .collect(toList());
+ }
+
+ private static List<String> canonicalBeanNamesFor(MemberVM member) {
+ return member.invoke(JMXMBeanReconnectDUnitTest::getLocalCanonicalBeanNames);
+ }
+
+ private static List<String> getLocalCanonicalBeanNames() {
+ Cache cache = ClusterStartupRule.getCache();
+ SystemManagementService service =
+ (SystemManagementService) ManagementService.getExistingManagementService(cache);
+ Map<ObjectName, Object> gfBeanMap = service.getJMXAdapter().getLocalGemFireMBean();
+ return gfBeanMap.keySet().stream()
+ .map(ObjectName::getCanonicalName)
+ .sorted()
+ .collect(toList());
+ }
+
+ private void waitForLocatorsToAgreeOnMembership() {
+ waitAtMost(1, MINUTES)
+ .until(
+ () -> {
+ int locator1BeanCount =
+ getFederatedGemfireBeansFrom(locator1)
+ .size();
+ int locator2BeanCount =
+ getFederatedGemfireBeansFrom(locator2)
+ .size();
+ return locator1BeanCount == locator2BeanCount;
+ });
+ }
+
+ private static Predicate<ObjectName> excludingBeansFor(String memberName) {
+ return b -> !b.getCanonicalName().contains("member=" + memberName);
+ }
+
+ private static String jmxBeanLocalhostUrlString(int port) {
+ return "service:jmx:rmi:///jndi/rmi://localhost"
+ + ":" + port + "/jmxrmi";
+ }
+
+ private Properties locator1Properties() {
+ Properties props = new Properties();
+ props.setProperty(ConfigurationProperties.JMX_MANAGER_HOSTNAME_FOR_CLIENTS, "localhost");
+ props.setProperty(ConfigurationProperties.JMX_MANAGER_PORT, "" + locator1JmxPort);
+ props.setProperty(ConfigurationProperties.NAME, LOCATOR_1_NAME);
+ props.setProperty(ConfigurationProperties.MAX_WAIT_TIME_RECONNECT, "5000");
+ return props;
+ }
+
+ private Properties locator2Properties() {
+ Properties props = new Properties();
+ props.setProperty(ConfigurationProperties.JMX_MANAGER_HOSTNAME_FOR_CLIENTS, "localhost");
+ props.setProperty(ConfigurationProperties.JMX_MANAGER_PORT, "" + locator2JmxPort);
+ props.setProperty(ConfigurationProperties.NAME, LOCATOR_2_NAME);
+ props.setProperty(ConfigurationProperties.LOCATORS, "localhost[" + locator1.getPort() + "]");
+ return props;
+ }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/MemberVM.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/MemberVM.java
index 7f2c095..eef2990 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/MemberVM.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/MemberVM.java
@@ -14,11 +14,18 @@
*/
package org.apache.geode.test.dunit.rules;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import java.io.File;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
+import org.awaitility.Awaitility;
+import org.apache.geode.distributed.internal.InternalDistributedSystem;
+import org.apache.geode.distributed.internal.InternalLocator;
+import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.junit.rules.Locator;
import org.apache.geode.test.junit.rules.Member;
@@ -102,6 +109,26 @@ public class MemberVM extends VMProvider implements Member {
vm.invoke(() -> ClusterStartupRule.memberStarter.forceDisconnectMember());
}
+ public void waitTilLocatorFullyReconnected() {
+ vm.invoke(() -> Awaitility.waitAtMost(30, TimeUnit.SECONDS).until(() -> {
+ InternalLocator intLocator = InternalLocator.getLocator();
+ InternalCache cache = ClusterStartupRule.getCache();
+ return intLocator != null && cache != null && intLocator.getDistributedSystem().isConnected()
+ && intLocator.isReconnected();
+ }));
+ }
+
+ public void waitTilServerFullyReconnected() {
+ vm.invoke(() -> Awaitility.waitAtMost(30, SECONDS).until(() -> {
+ InternalDistributedSystem internalDistributedSystem =
+ InternalDistributedSystem.getConnectedInstance();
+ return internalDistributedSystem != null
+ && internalDistributedSystem.getCache() != null
+ && !internalDistributedSystem.getCache().getCacheServers().isEmpty();
+ }));
+
+ }
+
/**
* this should called on a locatorVM or a serverVM with jmxManager enabled
*/
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleIntegrationTest.java
new file mode 100644
index 0000000..359fcaf
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleIntegrationTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.test.dunit.rules.tests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.distributed.internal.InternalLocator;
+import org.apache.geode.internal.AvailablePort;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.apache.geode.test.junit.rules.LocatorStarterRule;
+import org.apache.geode.test.junit.rules.ServerStarterRule;
+
+@Category(IntegrationTest.class)
+public class MemberStarterRuleIntegrationTest {
+
+
+ private LocatorStarterRule locator;
+ private ServerStarterRule server;
+
+ @After
+ public void cleanupAnyStartedMembers() {
+ if (locator != null) {
+ locator.after();
+ }
+ if (server != null) {
+ server.after();
+ }
+ }
+
+ @Test
+ public void testWithPortOnLocator() {
+ int targetPort = AvailablePort.getRandomAvailablePort(1);
+ locator = new LocatorStarterRule().withPort(targetPort).withAutoStart();
+ locator.before();
+
+ InternalLocator internalMember = locator.getLocator();
+
+ // This is the rule framework's port
+ assertThat(locator.getPort()).isEqualTo(targetPort);
+ // This is the actual, live member's port.
+ assertThat(internalMember.getPort()).isEqualTo(targetPort);
+
+ }
+
+ @Test
+ public void testWithPortOnServer() {
+ int targetPort = AvailablePort.getRandomAvailablePort(1);
+ server = new ServerStarterRule().withPort(targetPort).withAutoStart();
+ server.before();
+
+ CacheServer internalMember = server.getServer();
+
+ // This is the rule framework's port
+ assertThat(server.getPort()).isEqualTo(targetPort);
+ // This is the actual, live member's port.
+ assertThat(internalMember.getPort()).isEqualTo(targetPort);
+
+ }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleTest.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleTest.java
index 2ba751b..30d83bd 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleTest.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/MemberStarterRuleTest.java
@@ -68,6 +68,15 @@ public class MemberStarterRuleTest {
}
@Test
+ public void testWithPort() {
+ int targetPort = 12345;
+ locator = new LocatorStarterRule().withPort(targetPort);
+ locator.before();
+
+ assertThat(locator.getPort()).isEqualTo(targetPort);
+ }
+
+ @Test
public void testUseRandomPortByDefault() {
locator = new LocatorStarterRule();
locator.before();
diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/LocatorStarterRule.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/LocatorStarterRule.java
index f33e0eb..10e9a7a 100644
--- a/geode-core/src/test/java/org/apache/geode/test/junit/rules/LocatorStarterRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/LocatorStarterRule.java
@@ -77,11 +77,14 @@ public class LocatorStarterRule extends MemberStarterRule<LocatorStarterRule> im
public void startLocator() {
try {
// this will start a jmx manager and admin rest service by default
- locator = (InternalLocator) startLocatorAndDS(0, null, properties);
+ locator = (InternalLocator) startLocatorAndDS(memberPort, null, properties);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
+ // memberPort is by default zero, which translates to "randomly select an available port,"
+ // which is why it is updated here after being specified above.
memberPort = locator.getPort();
+
DistributionConfig config = locator.getConfig();
jmxPort = config.getJmxManagerPort();
httpPort = config.getHttpServicePort();
diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/MemberStarterRule.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/MemberStarterRule.java
index 58e5b12..b42cba0 100644
--- a/geode-core/src/test/java/org/apache/geode/test/junit/rules/MemberStarterRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/MemberStarterRule.java
@@ -63,7 +63,7 @@ public abstract class MemberStarterRule<T> extends SerializableExternalResource
protected transient TemporaryFolder temporaryFolder;
protected File workingDir;
- protected int memberPort = -1;
+ protected int memberPort = 0;
protected int jmxPort = -1;
protected int httpPort = -1;
@@ -105,6 +105,11 @@ public abstract class MemberStarterRule<T> extends SerializableExternalResource
}
}
+ public T withPort(int memberPort) {
+ this.memberPort = memberPort;
+ return (T) this;
+ }
+
public T withWorkingDir(File workingDir) {
this.workingDir = workingDir;
if (workingDir != null) {
diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/ServerStarterRule.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/ServerStarterRule.java
index 94dcb55..ca60fac 100644
--- a/geode-core/src/test/java/org/apache/geode/test/junit/rules/ServerStarterRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/ServerStarterRule.java
@@ -173,7 +173,9 @@ public class ServerStarterRule extends MemberStarterRule<ServerStarterRule> impl
DistributionConfig config =
((InternalDistributedSystem) cache.getDistributedSystem()).getConfig();
server = cache.addCacheServer();
- server.setPort(0);
+ // memberPort is by default zero, which translates to "randomly select an available port,"
+ // which is why it is updated after this try block
+ server.setPort(memberPort);
try {
server.start();
} catch (IOException e) {
diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/VMProvider.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/VMProvider.java
index f558b98..1b8f014 100644
--- a/geode-core/src/test/java/org/apache/geode/test/junit/rules/VMProvider.java
+++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/VMProvider.java
@@ -35,7 +35,7 @@ public abstract class VMProvider {
ClusterStartupRule.stopElementInsideVM();
MemberStarterRule.disconnectDSIfAny();
});
- };
+ }
public boolean isClient() {
return getVM().invoke(() -> {