This is an automated email from the ASF dual-hosted git repository. boglesby 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 2ae2b59 GEODE-5502: Removed duplicate / member-specific receivers from cluster configuration 2ae2b59 is described below commit 2ae2b591378a2cae8f5dee8f01bc07de8cce6d6e Author: Barry Oglesby <bogle...@users.noreply.github.com> AuthorDate: Mon Aug 6 13:53:32 2018 -0700 GEODE-5502: Removed duplicate / member-specific receivers from cluster configuration --- .../InternalConfigurationPersistenceService.java | 62 ++++- ...nternalConfigurationPersistenceServiceTest.java | 128 ++++++++++- .../test/dunit/standalone/VersionManager.java | 1 + .../cache/wan/WANRollingUpgradeDUnitTest.java | 43 +++- ...ipleReceiversDefinedInClusterConfiguration.java | 254 +++++++++++++++++++++ 5 files changed, 480 insertions(+), 8 deletions(-) diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java index a6d7380..fae9b74 100644 --- a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java +++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java @@ -128,7 +128,7 @@ public class InternalConfigurationPersistenceService implements ConfigurationPer /** * Name of the region which is used to store the configuration information */ - private static final String CONFIG_REGION_NAME = "_ConfigurationRegion"; + public static final String CONFIG_REGION_NAME = "_ConfigurationRegion"; private final String configDirPath; private final String configDiskDirPath; @@ -517,6 +517,7 @@ public class InternalConfigurationPersistenceService implements ConfigurationPer this.status.set(SharedConfigurationStatus.STARTED); Region<String, Configuration> configRegion = this.getConfigurationRegion(); lockSharedConfiguration(); + removeInvalidXmlConfigurations(configRegion); try { if (loadSharedConfigFromDir) { logger.info("Reading cluster configuration from '{}' directory", @@ -543,6 +544,65 @@ public class InternalConfigurationPersistenceService implements ConfigurationPer this.status.set(SharedConfigurationStatus.RUNNING); } + void removeInvalidXmlConfigurations(Region<String, Configuration> configRegion) + throws IOException, SAXException, ParserConfigurationException, TransformerException { + for (Map.Entry<String, Configuration> entry : configRegion.entrySet()) { + String group = entry.getKey(); + Configuration configuration = entry.getValue(); + String configurationXml = configuration.getCacheXmlContent(); + if (configurationXml != null && !configurationXml.isEmpty()) { + Document document = XmlUtils.createDocumentFromXml(configurationXml); + boolean removedInvalidReceivers = removeInvalidGatewayReceivers(document); + boolean removedDuplicateReceivers = removeDuplicateGatewayReceivers(document); + if (removedInvalidReceivers || removedDuplicateReceivers) { + configuration.setCacheXmlContent(XmlUtils.prettyXml(document)); + configRegion.put(group, configuration); + } + } + } + } + + boolean removeInvalidGatewayReceivers(Document document) throws TransformerException { + boolean modified = false; + NodeList receiverNodes = document.getElementsByTagName("gateway-receiver"); + for (int i = receiverNodes.getLength() - 1; i >= 0; i--) { + Element receiverElement = (Element) receiverNodes.item(i); + + // Check hostname-for-senders + String hostNameForSenders = receiverElement.getAttribute("hostname-for-senders"); + if (StringUtils.isNotBlank(hostNameForSenders)) { + receiverElement.getParentNode().removeChild(receiverElement); + logger.info("Removed invalid cluster configuration gateway-receiver element=" + + XmlUtils.prettyXml(receiverElement)); + modified = true; + } + + // Check bind-address + String bindAddress = receiverElement.getAttribute("bind-address"); + if (StringUtils.isNotBlank(bindAddress) && !bindAddress.equals("0.0.0.0")) { + receiverElement.getParentNode().removeChild(receiverElement); + logger.info("Removed invalid cluster configuration gateway-receiver element=" + + XmlUtils.prettyXml(receiverElement)); + modified = true; + } + } + return modified; + } + + boolean removeDuplicateGatewayReceivers(Document document) throws TransformerException { + boolean modified = false; + NodeList receiverNodes = document.getElementsByTagName("gateway-receiver"); + while (receiverNodes.getLength() > 1) { + Element receiverElement = (Element) receiverNodes.item(0); + receiverElement.getParentNode().removeChild(receiverElement); + logger.info("Removed duplicate cluster configuration gateway-receiver element=" + + XmlUtils.prettyXml(receiverElement)); + modified = true; + receiverNodes = document.getElementsByTagName("gateway-receiver"); + } + return modified; + } + private void persistSecuritySettings(final Region<String, Configuration> configRegion) { Properties securityProps = this.cache.getDistributedSystem().getSecurityProperties(); diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceServiceTest.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceServiceTest.java index e658fc9..6dbf018 100644 --- a/geode-core/src/test/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceServiceTest.java +++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceServiceTest.java @@ -17,6 +17,7 @@ package org.apache.geode.distributed.internal; +import static junitparams.JUnitParamsRunner.$; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -27,10 +28,19 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import java.util.AbstractMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.runner.RunWith; +import org.w3c.dom.Document; import org.apache.geode.cache.Region; import org.apache.geode.cache.configuration.CacheConfig; @@ -41,8 +51,9 @@ import org.apache.geode.internal.config.JAXBServiceTest.ElementOne; import org.apache.geode.internal.config.JAXBServiceTest.ElementTwo; import org.apache.geode.internal.lang.SystemPropertyHelper; import org.apache.geode.management.internal.configuration.domain.Configuration; +import org.apache.geode.management.internal.configuration.utils.XmlUtils; - +@RunWith(JUnitParamsRunner.class) public class InternalConfigurationPersistenceServiceTest { private InternalConfigurationPersistenceService service, service2; private Configuration configuration; @@ -193,4 +204,119 @@ public class InternalConfigurationPersistenceServiceTest { assertThat(packages).hasSize(2); assertThat(packages).contains("org.apache.geode", "io.pivotal"); } + + @Test + public void updateGatewayReceiverConfig() { + service.updateCacheConfig("cluster", cacheConfig -> { + CacheConfig.GatewayReceiver receiver = new CacheConfig.GatewayReceiver(); + cacheConfig.setGatewayReceiver(receiver); + return cacheConfig; + }); + + System.out.println(configuration.getCacheXmlContent()); + assertThat(configuration.getCacheXmlContent()).contains("<gateway-receiver/>"); + } + + @Test + public void removeDuplicateGatewayReceiversWithDefaultProperties() throws Exception { + Document document = + XmlUtils.createDocumentFromXml(getDuplicateReceiversWithDefaultPropertiesXml()); + System.out.println("Initial document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(2); + service.removeDuplicateGatewayReceivers(document); + System.out.println("Processed document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(1); + } + + @Test + public void removeInvalidGatewayReceiversWithDifferentHostNameForSenders() throws Exception { + Document document = + XmlUtils.createDocumentFromXml(getDuplicateReceiversWithDifferentHostNameForSendersXml()); + System.out.println("Initial document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(2); + service.removeInvalidGatewayReceivers(document); + System.out.println("Processed document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(0); + } + + @Test + public void removeInvalidGatewayReceiversWithDifferentBindAddresses() throws Exception { + Document document = + XmlUtils.createDocumentFromXml(getDuplicateReceiversWithDifferentBindAddressesXml()); + System.out.println("Initial document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(2); + service.removeInvalidGatewayReceivers(document); + System.out.println("Processed document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(0); + } + + @Test + public void keepValidGatewayReceiversWithDefaultBindAddress() throws Exception { + Document document = + XmlUtils.createDocumentFromXml(getSingleReceiverWithDefaultBindAddressXml()); + System.out.println("Initial document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(1); + service.removeInvalidGatewayReceivers(document); + System.out.println("Processed document:\n" + XmlUtils.prettyXml(document)); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()).isEqualTo(1); + } + + @Test + @Parameters(method = "getXmlAndExpectedElements") + public void removeInvalidXmlConfiguration(String xml, int expectedInitialElements, + int expectFinalElements) throws Exception { + Region<String, Configuration> configurationRegion = mock(Region.class); + configuration.setCacheXmlContent(xml); + System.out.println("Initial xml content:\n" + configuration.getCacheXmlContent()); + Document document = XmlUtils.createDocumentFromXml(configuration.getCacheXmlContent()); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()) + .isEqualTo(expectedInitialElements); + Set<Map.Entry<String, Configuration>> configurationEntries = new HashSet<>(); + configurationEntries.add(new AbstractMap.SimpleEntry<>("cluster", configuration)); + doReturn(configurationEntries).when(configurationRegion).entrySet(); + service.removeInvalidXmlConfigurations(configurationRegion); + System.out.println("Processed xml content:\n" + configuration.getCacheXmlContent()); + document = XmlUtils.createDocumentFromXml(configuration.getCacheXmlContent()); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()) + .isEqualTo(expectFinalElements); + } + + private String getDuplicateReceiversWithDefaultPropertiesXml() { + return "<cache>\n<gateway-receiver/>\n<gateway-receiver/>\n</cache>"; + } + + private String getDuplicateReceiversWithDifferentHostNameForSendersXml() { + return "<cache>\n<gateway-receiver hostname-for-senders=\"123.12.12.12\"/>\n<gateway-receiver hostname-for-senders=\"123.12.12.11\"/>\n</cache>"; + } + + private String getDuplicateReceiversWithDifferentBindAddressesXml() { + return "<cache>\n<gateway-receiver bind-address=\"123.12.12.12\"/>\n<gateway-receiver bind-address=\"123.12.12.11\"/>\n</cache>"; + } + + private String getSingleReceiverWithDefaultBindAddressXml() { + return "<cache>\n<gateway-receiver bind-address=\"0.0.0.0\"/>\n</cache>"; + } + + private String getDuplicateReceiversWithDefaultBindAddressesXml() { + return "<cache>\n<gateway-receiver bind-address=\"0.0.0.0\"/>\n<gateway-receiver bind-address=\"0.0.0.0\"/>\n</cache>"; + } + + private String getValidReceiversXml() { + return "<cache>\n<gateway-receiver/>\n</cache>"; + } + + private String getNoReceiversXml() { + return "<cache>\n</cache>"; + } + + protected Object[] getXmlAndExpectedElements() { + return $( + new Object[] {getDuplicateReceiversWithDefaultPropertiesXml(), 2, 1}, + new Object[] {getDuplicateReceiversWithDifferentHostNameForSendersXml(), 2, 0}, + new Object[] {getDuplicateReceiversWithDifferentBindAddressesXml(), 2, 0}, + new Object[] {getSingleReceiverWithDefaultBindAddressXml(), 1, 1}, + new Object[] {getDuplicateReceiversWithDefaultBindAddressesXml(), 2, 1}, + new Object[] {getValidReceiversXml(), 1, 1}, + new Object[] {getNoReceiversXml(), 0, 0}); + } } diff --git a/geode-junit/src/main/java/org/apache/geode/test/dunit/standalone/VersionManager.java b/geode-junit/src/main/java/org/apache/geode/test/dunit/standalone/VersionManager.java index b711193..942a939 100755 --- a/geode-junit/src/main/java/org/apache/geode/test/dunit/standalone/VersionManager.java +++ b/geode-junit/src/main/java/org/apache/geode/test/dunit/standalone/VersionManager.java @@ -39,6 +39,7 @@ public class VersionManager { public static final String GEODE_110 = "110"; public static final String GEODE_120 = "120"; public static final String GEODE_130 = "130"; + public static final String GEODE_140 = "140"; private static VersionManager instance; diff --git a/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeDUnitTest.java b/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeDUnitTest.java index e9c55ba..3263237 100644 --- a/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeDUnitTest.java +++ b/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeDUnitTest.java @@ -23,6 +23,7 @@ import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS; import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL; import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; import static org.apache.geode.distributed.ConfigurationProperties.REMOTE_LOCATORS; +import static org.apache.geode.distributed.ConfigurationProperties.USE_CLUSTER_CONFIGURATION; import static org.junit.Assert.assertEquals; import java.io.IOException; @@ -72,7 +73,7 @@ import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactor @Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class) public abstract class WANRollingUpgradeDUnitTest extends JUnit4CacheTestCase { @Parameterized.Parameters(name = "from_v{0}") - public static Collection<String> data() { + public static Collection data() { List<String> result = VersionManager.getInstance().getVersionsWithoutCurrent(); if (result.size() < 1) { throw new RuntimeException("No older versions of Geode were found to test against"); @@ -89,9 +90,16 @@ public abstract class WANRollingUpgradeDUnitTest extends JUnit4CacheTestCase { @Rule public transient GfshCommandRule gfsh = new GfshCommandRule(); - public void startLocator(int port, int distributedSystemId, String locators, + void startLocator(int port, int distributedSystemId, String locators, String remoteLocators) throws IOException { - Properties props = getLocatorProperties(distributedSystemId, locators, remoteLocators); + startLocator(port, distributedSystemId, locators, + remoteLocators, false); + } + + void startLocator(int port, int distributedSystemId, String locators, + String remoteLocators, boolean enableClusterConfiguration) throws IOException { + Properties props = getLocatorProperties(distributedSystemId, locators, remoteLocators, + enableClusterConfiguration); Locator.startLocatorAndDS(port, null, props); } @@ -108,13 +116,22 @@ public abstract class WANRollingUpgradeDUnitTest extends JUnit4CacheTestCase { private Properties getLocatorProperties(int distributedSystemId, String locators, String remoteLocators) { + return getLocatorProperties(distributedSystemId, locators, + remoteLocators, false); + } + + + private Properties getLocatorProperties(int distributedSystemId, String locators, + String remoteLocators, boolean enableClusterConfiguration) { Properties props = new Properties(); props.setProperty(MCAST_PORT, "0"); props.setProperty(DISTRIBUTED_SYSTEM_ID, String.valueOf(distributedSystemId)); props.setProperty(LOCATORS, locators); - props.setProperty(REMOTE_LOCATORS, remoteLocators); + if (remoteLocators != null) { + props.setProperty(REMOTE_LOCATORS, remoteLocators); + } props.setProperty(LOG_LEVEL, DUnitLauncher.logLevel); - props.setProperty(ENABLE_CLUSTER_CONFIGURATION, "false"); + props.setProperty(ENABLE_CLUSTER_CONFIGURATION, String.valueOf(enableClusterConfiguration)); return props; } @@ -124,9 +141,16 @@ public abstract class WANRollingUpgradeDUnitTest extends JUnit4CacheTestCase { VM rollLocatorToCurrent(VM rollLocator, int port, int distributedSystemId, String locators, String remoteLocators) { + return rollLocatorToCurrent(rollLocator, port, distributedSystemId, + locators, remoteLocators, false); + } + + VM rollLocatorToCurrent(VM rollLocator, int port, int distributedSystemId, + String locators, String remoteLocators, boolean enableClusterConfiguration) { rollLocator.invoke(() -> stopLocator()); VM newLocator = Host.getHost(0).getVM(VersionManager.CURRENT_VERSION, rollLocator.getId()); - newLocator.invoke(() -> startLocator(port, distributedSystemId, locators, remoteLocators)); + newLocator.invoke(() -> startLocator(port, distributedSystemId, locators, remoteLocators, + enableClusterConfiguration)); return newLocator; } @@ -219,7 +243,14 @@ public abstract class WANRollingUpgradeDUnitTest extends JUnit4CacheTestCase { } public void createCache(String locators) { + createCache(locators, false, false); + } + + public void createCache(String locators, boolean enableClusterConfiguration, + boolean useClusterConfiguration) { Properties props = new Properties(); + props.setProperty(ENABLE_CLUSTER_CONFIGURATION, String.valueOf(enableClusterConfiguration)); + props.setProperty(USE_CLUSTER_CONFIGURATION, String.valueOf(useClusterConfiguration)); props.setProperty(MCAST_PORT, "0"); props.setProperty(LOCATORS, locators); props.setProperty(LOG_LEVEL, DUnitLauncher.logLevel); diff --git a/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeMultipleReceiversDefinedInClusterConfiguration.java b/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeMultipleReceiversDefinedInClusterConfiguration.java new file mode 100644 index 0000000..aecd3e5 --- /dev/null +++ b/geode-wan/src/upgradeTest/java/org/apache/geode/cache/wan/WANRollingUpgradeMultipleReceiversDefinedInClusterConfiguration.java @@ -0,0 +1,254 @@ +/* + * 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.cache.wan; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.awaitility.Awaitility; +import org.junit.Test; +import org.junit.runners.Parameterized; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.Region; +import org.apache.geode.distributed.ConfigurationPersistenceService; +import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService; +import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.internal.AvailablePort; +import org.apache.geode.internal.cache.xmlcache.CacheCreation; +import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator; +import org.apache.geode.management.internal.configuration.domain.Configuration; +import org.apache.geode.management.internal.configuration.utils.XmlUtils; +import org.apache.geode.test.dunit.DistributedTestUtils; +import org.apache.geode.test.dunit.Host; +import org.apache.geode.test.dunit.NetworkUtils; +import org.apache.geode.test.dunit.VM; +import org.apache.geode.test.dunit.standalone.VersionManager; + +public class WANRollingUpgradeMultipleReceiversDefinedInClusterConfiguration + extends WANRollingUpgradeDUnitTest { + + @Parameterized.Parameter(1) + public List<Attribute> attributes; + + @Parameterized.Parameter(2) + public int expectedReceiverElements; + + @Parameterized.Parameter(3) + public int expectedReceivers; + + @Parameterized.Parameters(name = "from_v{0}; attributes={1}; expectedReceiverCount={2}") + public static Collection data() { + // Get initial versions to test against + List<String> versions = getVersionsToTest(); + + // Build up a list of version->attributes->expectedReceivers + List<Object[]> result = new ArrayList<>(); + versions.forEach(version -> { + // Add a case for hostname-for-senders + addReceiversWithHostNameForSenders(result, version); + + // Add a case for bind-address + addReceiversWithBindAddresses(result, version); + + // Add a case for multiple receivers with default attributes + addMultipleReceiversWithDefaultAttributes(result, version); + + // Add a case for single receiver with default bind-address + addSingleReceiverWithDefaultBindAddress(result, version); + + // Add a case for single receiver with default attributes + addSingleReceiverWithDefaultAttributes(result, version); + }); + + System.out.println("running against these versions and attributes: " + + result.stream().map(entry -> Arrays.toString(entry)).collect( + Collectors.joining(", "))); + return result; + } + + private static List<String> getVersionsToTest() { + // There is no need to test old versions beyond 130. Individual member configuration is not + // saved in cluster configuration and multiple receivers are not supported starting in 140. + // Note: This comparison works because '130' < '140'. + List<String> result = VersionManager.getInstance().getVersionsWithoutCurrent(); + result.removeIf(version -> (version.compareTo(VersionManager.GEODE_140) >= 0)); + if (result.size() < 1) { + throw new RuntimeException("No older versions of Geode were found to test against"); + } + return result; + } + + private static void addReceiversWithHostNameForSenders(List<Object[]> result, String version) { + List<Attribute> attributes = new ArrayList<>(); + attributes.add(new Attribute("hostname-for-senders", "121.21.21.21")); + attributes.add(new Attribute("hostname-for-senders", "121.21.21.22")); + result.add(new Object[] {version, attributes, 2, 0}); + } + + private static void addReceiversWithBindAddresses(List<Object[]> result, String version) { + List<Attribute> attributes = new ArrayList<>(); + attributes.add(new Attribute("bind-address", "121.21.21.21")); + attributes.add(new Attribute("bind-address", "121.21.21.22")); + result.add(new Object[] {version, attributes, 2, 0}); + } + + private static void addMultipleReceiversWithDefaultAttributes(List<Object[]> result, + String version) { + List<Attribute> attributes = new ArrayList<>(); + attributes.add(Attribute.DEFAULT); + attributes.add(Attribute.DEFAULT); + result.add(new Object[] {version, attributes, 2, 1}); + } + + private static void addSingleReceiverWithDefaultAttributes(List<Object[]> result, + String version) { + List<Attribute> attributes = new ArrayList<>(); + attributes.add(Attribute.DEFAULT); + result.add(new Object[] {version, attributes, 1, 1}); + } + + private static void addSingleReceiverWithDefaultBindAddress(List<Object[]> result, + String version) { + List<Attribute> attributes = new ArrayList<>(); + attributes.add(new Attribute("bind-address", "0.0.0.0")); + result.add(new Object[] {version, attributes, 1, 1}); + } + + @Test + public void testMultipleReceiversRemovedDuringRoll() throws Exception { + // Get old locator properties + VM locator = Host.getHost(0).getVM(oldVersion, 0); + String hostName = NetworkUtils.getServerHostName(); + final int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); + DistributedTestUtils.deleteLocatorStateFile(locatorPort); + final String locators = hostName + "[" + locatorPort + "]"; + + // Start old locator + locator.invoke(() -> startLocator(locatorPort, 0, + locators, null, true)); + + // Wait for configuration configuration to be ready. + locator.invoke( + () -> Awaitility.await().atMost(65, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS) + .until(() -> assertThat( + InternalLocator.getLocator().isSharedConfigurationRunning()).isTrue())); + + // Add cluster configuration elements containing multiple receivers + locator.invoke( + () -> addMultipleGatewayReceiverElementsToClusterConfiguration()); + + // Roll old locator to current + rollLocatorToCurrent(locator, locatorPort, 0, locators, + null, true); + + // Verify cluster configuration contains expected number of receivers + locator.invoke(() -> verifyGatewayReceiverClusterConfigurationElements()); + + // Start member in current version with cluster configuration enabled + VM server = Host.getHost(0).getVM(VersionManager.CURRENT_VERSION, 1); + server.invoke(() -> createCache(locators, true, true)); + + // Verify member has expected number of receivers + server.invoke(() -> verifyGatewayReceivers()); + } + + private void addMultipleGatewayReceiverElementsToClusterConfiguration() + throws Exception { + // Create empty xml document + CacheCreation creation = new CacheCreation(); + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(stringWriter); + CacheXmlGenerator.generate(creation, printWriter, true, false, false); + printWriter.close(); + String baseXml = stringWriter.toString(); + Document document = XmlUtils.createDocumentFromXml(baseXml); + + // Add gateway-receiver for each attribute + for (Attribute attribute : attributes) { + Node rootNode = document.getDocumentElement(); + Element receiverElement = document.createElement("gateway-receiver"); + if (!attribute.name.equals("default")) { + receiverElement.setAttribute(attribute.name, attribute.value); + } + rootNode.appendChild(receiverElement); + } + assertThat(document.getElementsByTagName("gateway-receiver").getLength()) + .isEqualTo(expectedReceiverElements); + + // Get configuration region + Region<String, Configuration> configurationRegion = CacheFactory.getAnyInstance().getRegion( + InternalConfigurationPersistenceService.CONFIG_REGION_NAME); + + // Create a configuration and put into the configuration region + Configuration configuration = new Configuration(ConfigurationPersistenceService.CLUSTER_CONFIG); + configuration.setCacheXmlContent(XmlUtils.prettyXml(document)); + configurationRegion.put(ConfigurationPersistenceService.CLUSTER_CONFIG, configuration); + } + + private void verifyGatewayReceiverClusterConfigurationElements() throws Exception { + // Get configuration region + Region<String, Configuration> configurationRegion = CacheFactory.getAnyInstance().getRegion( + InternalConfigurationPersistenceService.CONFIG_REGION_NAME); + + // Get the configuration from the region + Configuration configuration = + configurationRegion.get(ConfigurationPersistenceService.CLUSTER_CONFIG); + + // Verify the configuration contains no gateway-receiver elements + Document document = XmlUtils.createDocumentFromXml(configuration.getCacheXmlContent()); + assertThat(document.getElementsByTagName("gateway-receiver").getLength()) + .isEqualTo(expectedReceivers); + } + + private void verifyGatewayReceivers() { + assertThat(CacheFactory.getAnyInstance().getGatewayReceivers().size()) + .isEqualTo(expectedReceivers); + } + + private static class Attribute implements Serializable { + + private String name; + + private String value; + + private static final Attribute DEFAULT = new Attribute("default", ""); + + Attribute(String name, String value) { + this.name = name; + this.value = value; + } + + public String toString() { + return new StringBuilder() + .append(this.name) + .append("=") + .append(this.value) + .toString(); + } + } +}