/* * 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.distributed; import org.apache.geode.CancelException; import org.apache.geode.GemFireConfigException; import org.apache.geode.SystemConnectException; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheFactory; import org.apache.geode.cache.Region; import org.apache.geode.cache30.CacheSerializableRunnable; import org.apache.geode.distributed.internal.*; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.distributed.internal.membership.gms.MembershipManagerHelper; import org.apache.geode.distributed.internal.membership.gms.messenger.JGroupsMessenger; import org.apache.geode.distributed.internal.membership.gms.mgr.GMSMembershipManager; import org.apache.geode.internal.AvailablePort; import org.apache.geode.internal.AvailablePortHelper; import org.apache.geode.internal.net.SocketCreator; import org.apache.geode.test.dunit.*; import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase; import org.apache.geode.test.junit.categories.DistributedTest; import org.apache.geode.test.junit.categories.FlakyTest; import org.apache.geode.test.junit.categories.MembershipTest; import org.junit.*; import org.junit.experimental.categories.Category; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.TimeoutException; import static org.apache.geode.distributed.ConfigurationProperties.*; import static org.apache.geode.internal.AvailablePort.SOCKET; import static org.junit.Assert.*; /** * Tests the functionality of the {@link DistributedSystem} class. * * @see InternalDistributedSystemJUnitTest * */ @Category({DistributedTest.class, MembershipTest.class}) public class DistributedSystemDUnitTest extends JUnit4DistributedTestCase { public void postSetUp() throws Exception { disconnectAllFromDS(); } /** * ensure that waitForMemberDeparture correctly flushes the serial message queue for the given * member */ @Test public void testWaitForDeparture() throws Exception { disconnectAllFromDS(); int locatorPort = AvailablePort.getRandomAvailablePort(SOCKET); Properties p = getDistributedSystemProperties(); p.put(LOCATORS, ""); p.put(START_LOCATOR, "localhost[" + locatorPort + "]"); p.put(DISABLE_TCP, "true"); InternalDistributedSystem ds = (InternalDistributedSystem) DistributedSystem.connect(p); try { // construct a member ID that will represent a departed member InternalDistributedMember mbr = new InternalDistributedMember("localhost", 12345, "", "", DistributionManager.NORMAL_DM_TYPE, null, null); final DistributionManager mgr = (DistributionManager) ds.getDistributionManager(); // schedule a message in order to create a queue for the fake member final FakeMessage msg = new FakeMessage(null); mgr.getExecutor(DistributionManager.SERIAL_EXECUTOR, mbr).execute(new SizeableRunnable(100) { public void run() { msg.doAction(mgr, false); } public String toString() { return "Processing fake message"; } }); try { assertTrue("expected the serial queue to be flushed", mgr.getMembershipManager().waitForDeparture(mbr)); } catch (InterruptedException e) { fail("interrupted"); } catch (TimeoutException e) { fail("timed out - increase this test's member-timeout setting"); } } finally { ds.disconnect(); } } private static class FakeMessage extends SerialDistributionMessage { volatile boolean[] blocked; volatile boolean processed; FakeMessage(boolean[] blocked) { this.blocked = blocked; } public void doAction(DistributionManager dm, boolean block) { processed = true; if (block) { synchronized (blocked) { blocked[0] = true; blocked.notify(); try { blocked.wait(60000); } catch (InterruptedException e) { } } } } public int getDSFID() { return 0; // never serialized } protected void process(DistributionManager dm) { // this is never called } public String toString() { return "FakeMessage(blocking=" + (blocked != null) + ")"; } } /** * Tests that we can get a DistributedSystem with the same configuration twice. */ @Test public void testGetSameSystemTwice() { Properties config = new Properties(); config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); // set a flow-control property for the test (bug 37562) config.setProperty(MCAST_FLOW_CONTROL, "3000000,0.20,3000"); DistributedSystem system1 = DistributedSystem.connect(config); DistributedSystem system2 = DistributedSystem.connect(config); assertSame(system1, system2); system1.disconnect(); } /** * Tests that getting a <code>DistributedSystem</code> with a different configuration after one * has already been obtained throws an exception. */ @Test public void testGetDifferentSystem() { Properties config = new Properties(); config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); config.setProperty(MCAST_FLOW_CONTROL, "3000000,0.20,3000"); DistributedSystem system1 = DistributedSystem.connect(config); config.setProperty(MCAST_ADDRESS, "224.0.0.1"); try { DistributedSystem.connect(config); if (System.getProperty(DistributionConfig.GEMFIRE_PREFIX + "mcast-address") == null) { fail("Should have thrown an IllegalStateException"); } } catch (IllegalStateException ex) { // pass... } finally { system1.disconnect(); } } /** * Tests getting a system with a different configuration after another system has been closed. */ @Test public void testGetDifferentSystemAfterClose() { Properties config = new Properties(); config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); DistributedSystem system1 = DistributedSystem.connect(config); system1.disconnect(); int time = DistributionConfig.DEFAULT_ACK_WAIT_THRESHOLD + 17; config.put(ACK_WAIT_THRESHOLD, String.valueOf(time)); DistributedSystem system2 = DistributedSystem.connect(config); system2.disconnect(); } @Test public void testGetProperties() { Properties config = new Properties(); // a loner is all this test needs int unusedPort = 0; config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); DistributedSystem system1 = DistributedSystem.connect(config); assertTrue(config != system1.getProperties()); assertEquals(unusedPort, Integer.parseInt(system1.getProperties().getProperty(MCAST_PORT))); system1.disconnect(); assertTrue(config != system1.getProperties()); assertEquals(unusedPort, Integer.parseInt(system1.getProperties().getProperty(MCAST_PORT))); } @Test public void testIsolatedDistributedSystem() throws Exception { Properties config = new Properties(); config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); getSystem(config); try { // make sure isolated distributed system can still create a cache and region Cache cache = CacheFactory.create(getSystem()); Region r = cache.createRegion(getUniqueName(), new AttributesFactory().create()); r.put("test", "value"); assertEquals("value", r.get("test")); } finally { getSystem().disconnect(); } } /** test the ability to set the port used to listen for tcp/ip connections */ @Test public void testSpecificTcpPort() throws Exception { Properties config = new Properties(); int tcpPort = AvailablePort.getRandomAvailablePort(SOCKET); config.put(LOCATORS, "localhost[" + DistributedTestUtils.getDUnitLocatorPort() + "]"); config.setProperty(TCP_PORT, String.valueOf(tcpPort)); InternalDistributedSystem system = getSystem(config); DistributionManager dm = (DistributionManager) system.getDistributionManager(); GMSMembershipManager mgr = (GMSMembershipManager) dm.getMembershipManager(); int actualPort = mgr.getDirectChannelPort(); system.disconnect(); assertEquals(tcpPort, actualPort); } /** * test that loopback cannot be used as a bind address when a locator w/o a bind address is being * used */ @Test public void testLoopbackNotAllowed() throws Exception { // DISABLED for bug #49926 InetAddress loopback = null; for (Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces(); it .hasMoreElements();) { NetworkInterface nif = it.nextElement(); for (Enumeration<InetAddress> ait = nif.getInetAddresses(); ait.hasMoreElements();) { InetAddress a = ait.nextElement(); Class theClass = SocketCreator.getLocalHost() instanceof Inet4Address ? Inet4Address.class : Inet6Address.class; if (a.isLoopbackAddress() && (a.getClass().isAssignableFrom(theClass))) { loopback = a; break; } } } if (loopback != null) { Properties config = new Properties(); config.put(MCAST_PORT, "0"); String locators = InetAddress.getLocalHost().getHostName() + "[" + DistributedTestUtils.getDUnitLocatorPort() + "]"; config.put(LOCATORS, locators); config.setProperty(BIND_ADDRESS, loopback.getHostAddress()); LogWriterUtils.getLogWriter() .info("attempting to connect with " + loopback + " and locators=" + locators); try { InternalDistributedSystem system = getSystem(config); system.disconnect(); fail("expected a configuration exception disallowing use of loopback address"); } catch (GemFireConfigException e) { // expected } catch (DistributionException e) { // expected } } } @Test public void testUDPPortRange() throws Exception { Properties config = new Properties(); int unicastPort = AvailablePort.getRandomAvailablePort(SOCKET, AvailablePort.getAddress(SOCKET), true); config.put(LOCATORS, "localhost[" + DistributedTestUtils.getDUnitLocatorPort() + "]"); // Minimum 3 ports required in range for UDP, FD_SOCK and TcpConduit. config.setProperty(MEMBERSHIP_PORT_RANGE, "" + unicastPort + "-" + (unicastPort + 2)); InternalDistributedSystem system = getSystem(config); DistributionManager dm = (DistributionManager) system.getDistributionManager(); InternalDistributedMember idm = dm.getDistributionManagerId(); system.disconnect(); assertTrue(unicastPort <= idm.getPort() && idm.getPort() <= unicastPort + 2); assertTrue(unicastPort <= idm.getPort() && idm.getDirectChannelPort() <= unicastPort + 2); } /*** * this will return starting port, from it "range" of port will available * * @param range * @return */ private int getPortRange(int range) { int port = DistributionConfig.DEFAULT_MEMBERSHIP_PORT_RANGE[0] + 10000; int startPort = port; int found = 0; while (port <= DistributionConfig.DEFAULT_MEMBERSHIP_PORT_RANGE[1]) { if (AvailablePort.isPortAvailable(port, SOCKET)) { found++; if (found == range) { break; } port++; } else { port++; startPort = port; found = 0; } } if (port > DistributionConfig.DEFAULT_MEMBERSHIP_PORT_RANGE[1]) { fail("Unable to find port range " + range); } return startPort; } @Test public void testMembershipPortRangeWithExactThreeValues() throws Exception { Properties config = new Properties(); config.setProperty(LOCATORS, "localhost[" + DistributedTestUtils.getDUnitLocatorPort() + "]"); int portRange = 3; int portStartRange = getPortRange(portRange); int portEndRange = portStartRange + portRange - 1; config.setProperty(MEMBERSHIP_PORT_RANGE, "" + (portStartRange) + "-" + (portEndRange)); InternalDistributedSystem system = getSystem(config); Cache cache = CacheFactory.create(system); cache.addCacheServer(); DistributionManager dm = (DistributionManager) system.getDistributionManager(); InternalDistributedMember idm = dm.getDistributionManagerId(); GMSMembershipManager manager = (GMSMembershipManager) MembershipManagerHelper.getMembershipManager(system); JGroupsMessenger messenger = (JGroupsMessenger) manager.getServices().getMessenger(); String jgConfig = messenger.getJGroupsStackConfig(); system.disconnect(); assertTrue("expected to find port_range=\"2\" in " + jgConfig, jgConfig.contains("port_range=\"2\"")); assertTrue(idm.getPort() <= portEndRange); assertTrue(idm.getPort() >= portStartRange); assertTrue(idm.getDirectChannelPort() <= portEndRange); assertTrue(idm.getDirectChannelPort() >= portStartRange); } @Category(FlakyTest.class) // GEODE-1198 @Test public void testConflictingUDPPort() throws Exception { final Properties config = new Properties(); final int mcastPort = AvailablePort.getRandomAvailablePort(AvailablePort.MULTICAST); final int[] socketPorts = AvailablePortHelper.getRandomAvailableTCPPorts(1, true); final int unicastPort = getPortRange(3); config.setProperty(MCAST_PORT, String.valueOf(mcastPort)); config.setProperty(START_LOCATOR, "localhost[" + socketPorts[0] + "]"); config.setProperty(MEMBERSHIP_PORT_RANGE, "" + unicastPort + "-" + (unicastPort + 2)); InternalDistributedSystem system = (InternalDistributedSystem) DistributedSystem.connect(config); try { DistributionManager dm = (DistributionManager) system.getDistributionManager(); InternalDistributedMember idm = dm.getDistributionManagerId(); VM vm = Host.getHost(0).getVM(1); vm.invoke(new CacheSerializableRunnable("start conflicting system") { public void run2() { try { String locators = (String) config.remove(START_LOCATOR); config.put(LOCATORS, locators); DistributedSystem system = DistributedSystem.connect(config); system.disconnect(); } catch (GemFireConfigException e) { return; // } catch (RMIException e) { if (e.getCause() instanceof SystemConnectException) { // GEODE-1198: for this test, the membership-port-range has only 3 ports available. // If in some rare cases, one of the ports is used by others, it will get this // exception. So just ignore it. Since adding one more port will also fail the test. return; } } fail("expected a GemFireConfigException but didn't get one"); } }); } finally { system.disconnect(); } } /** * Tests that configuring a distributed system with a cache-xml-file of "" does not initialize a * cache. See bug 32254. * * @since GemFire 4.0 */ @Test public void testEmptyCacheXmlFile() throws Exception { Properties config = new Properties(); config.setProperty(MCAST_PORT, "0"); config.setProperty(LOCATORS, ""); config.setProperty(CACHE_XML_FILE, ""); DistributedSystem sys = DistributedSystem.connect(config); try { try { CacheFactory.getInstance(sys); fail("Should have thrown a CancelException"); } catch (CancelException expected) { } // now make sure we can create the cache CacheFactory.create(sys); } finally { sys.disconnect(); } } }