/*
* 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.client.internal;
import static org.junit.Assert.*;
import java.io.*;
import java.net.*;
import java.util.*;
import org.junit.Assert;
import org.junit.*;
import org.junit.experimental.categories.*;
import org.apache.geode.cache.*;
import org.apache.geode.cache.client.*;
import org.apache.geode.cache.server.*;
import org.apache.geode.distributed.internal.*;
import org.apache.geode.internal.*;
import org.apache.geode.internal.cache.*;
import org.apache.geode.management.membership.*;
import org.apache.geode.test.dunit.*;
import org.apache.geode.test.junit.categories.*;
/**
* Tests cases that are particular for the auto connection source - dynamically discovering servers,
* locators, handling locator disappearance, etc.
*/
@Category({DistributedTest.class, ClientServerTest.class})
public class AutoConnectionSourceDUnitTest extends LocatorTestBase {
protected static final Object BRIDGE_LISTENER = "BRIDGE_LISTENER";
private static final long MAX_WAIT = 60000;
@Override
public final void postSetUp() throws Exception {
IgnoredException.addIgnoredException("NoAvailableLocatorsException");
}
public AutoConnectionSourceDUnitTest() {
super();
}
@Test
public void testDiscoverBridgeServers() throws Exception {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
String hostName = NetworkUtils.getServerHostName(vm0.getHost());
vm0.invoke("Start Locator", () -> startLocator(hostName, locatorPort, ""));
String locators = NetworkUtils.getServerHostName(vm0.getHost()) + "[" + locatorPort + "]";
vm1.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
vm2.invoke("StartBridgeClient",
() -> startBridgeClient(null, NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
putAndWaitForSuccess(vm2, REGION_NAME, "key", "value");
Assert.assertEquals("value", getInVM(vm1, "key"));
}
@Test
public void testNoLocators() {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
try {
vm0.invoke("StartBridgeClient",
() -> startBridgeClient(null, NetworkUtils.getServerHostName(vm0.getHost()),
AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET)));
putInVM(vm0, "key", "value");
fail("Client cache should not have been able to start");
} catch (Exception e) {
// expected an exception
}
}
@Test
public void testNoBridgeServer() {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
String hostName = NetworkUtils.getServerHostName(vm0.getHost());
vm0.invoke("Start Locator", () -> startLocator(hostName, locatorPort, ""));
try {
vm1.invoke("StartBridgeClient", () -> startBridgeClient(null,
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
putInVM(vm0, "key", "value");
fail("Client cache should not have been able to start");
} catch (Exception e) {
// expected an exception
}
}
@Test
public void testDynamicallyFindBridgeServer() throws Exception {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
String hostName = NetworkUtils.getServerHostName(vm0.getHost());
vm0.invoke("Start Locator", () -> startLocator(hostName, locatorPort, ""));
String locators = NetworkUtils.getServerHostName(vm0.getHost()) + "[" + locatorPort + "]";
vm1.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
vm2.invoke("StartBridgeClient",
() -> startBridgeClient(null, NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
putAndWaitForSuccess(vm2, REGION_NAME, "key", "value");
vm3.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
stopBridgeMemberVM(vm1);
putAndWaitForSuccess(vm2, REGION_NAME, "key2", "value2");
Assert.assertEquals("value2", getInVM(vm3, "key2"));
}
@Test
public void testDynamicallyFindLocators() throws Exception {
try {
final Host host = Host.getHost(0);
final String hostName = NetworkUtils.getServerHostName(host);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(3);
final int locatorPort0 = ports[0];
final int locatorPort1 = ports[1];
final int locatorPort3 = ports[2];
String locators =
getLocatorString(host, new int[] {locatorPort0, locatorPort1, locatorPort3});
vm0.invoke("Start Locator", () -> startLocator(NetworkUtils.getServerHostName(vm0.getHost()),
locatorPort0, locators));
vm1.invoke("Start Locator", () -> startLocator(NetworkUtils.getServerHostName(vm1.getHost()),
locatorPort1, locators));
vm2.invoke("StartBridgeClient", () -> startBridgeClient(null,
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort0));
InetSocketAddress locatorToWaitFor = new InetSocketAddress(hostName, locatorPort1);
waitForLocatorDiscovery(vm2, locatorToWaitFor);
vm0.invoke("Stop Locator", () -> stopLocator());
vm0.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
putAndWaitForSuccess(vm2, REGION_NAME, "key", "value");
Assert.assertEquals("value", getInVM(vm0, "key"));
vm3.invoke("Start Locator", () -> startLocator(NetworkUtils.getServerHostName(vm3.getHost()),
locatorPort3, locators));
stopBridgeMemberVM(vm0);
locatorToWaitFor = new InetSocketAddress(hostName, locatorPort3);
waitForLocatorDiscovery(vm2, locatorToWaitFor);
vm1.invoke("Stop Locator", () -> stopLocator());
vm1.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
putAndWaitForSuccess(vm2, REGION_NAME, "key2", "value2");
Assert.assertEquals("value2", getInVM(vm1, "key2"));
} catch (Exception ec) {
if (ec.getCause() != null && (ec.getCause().getCause() instanceof BindException))
return;// BindException let it pass
throw ec;
}
}
@Test
public void testEmbeddedLocator() throws Exception {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
String locators = NetworkUtils.getServerHostName(vm0.getHost()) + "[" + locatorPort + "]";
vm0.invoke("Start BridgeServer", () -> startBridgeServerWithEmbeddedLocator(null, locators,
new String[] {REGION_NAME}, CacheServer.DEFAULT_LOAD_PROBE));
vm2.invoke("StartBridgeClient",
() -> startBridgeClient(null, NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
putAndWaitForSuccess(vm2, REGION_NAME, "key", "value");
Assert.assertEquals("value", getInVM(vm2, "key"));
}
private void waitForLocatorDiscovery(VM vm, final InetSocketAddress locatorToWaitFor) {
vm.invoke(new SerializableCallable() {
public Object call() throws InterruptedException {
MyLocatorCallback callback = (MyLocatorCallback) remoteObjects.get(CALLBACK_KEY);
boolean discovered = callback.waitForDiscovery(locatorToWaitFor, MAX_WAIT);
Assert.assertTrue(
"Waited " + MAX_WAIT + " for " + locatorToWaitFor
+ " to be discovered on client. List is now: " + callback.getDiscovered(),
discovered);
return null;
}
});
}
@Test
public void testServerGroups() throws Exception {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
vm0.invoke("Start Locator",
() -> startLocator(NetworkUtils.getServerHostName(vm0.getHost()), locatorPort, ""));
String locators = NetworkUtils.getServerHostName(vm0.getHost()) + "[" + locatorPort + "]";
vm1.invoke("Start BridgeServer", () -> startBridgeServer(new String[] {"group1", "group2"},
locators, new String[] {"A", "B"}));
vm2.invoke("Start BridgeServer", () -> startBridgeServer(new String[] {"group2", "group3"},
locators, new String[] {"B", "C"}));
vm3.invoke("StartBridgeClient", () -> startBridgeClient("group1",
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort, new String[] {"A", "B", "C"}));
putAndWaitForSuccess(vm3, "A", "key", "value");
Assert.assertEquals("value", getInVM(vm1, "A", "key"));
try {
putInVM(vm3, "C", "key2", "value2");
fail("Should not have been able to find Region C on the server");
} catch (Exception expected) {
}
stopBridgeMemberVM(vm3);
vm3.invoke("StartBridgeClient", () -> startBridgeClient("group3",
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort, new String[] {"A", "B", "C"}));
try {
putInVM(vm3, "A", "key3", "value");
fail("Should not have been able to find Region A on the server");
} catch (Exception expected) {
}
putInVM(vm3, "C", "key4", "value");
Assert.assertEquals("value", getInVM(vm2, "C", "key4"));
stopBridgeMemberVM(vm3);
vm3.invoke("StartBridgeClient", () -> startBridgeClient("group2",
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort, new String[] {"A", "B", "C"}));
putInVM(vm3, "B", "key5", "value");
Assert.assertEquals("value", getInVM(vm1, "B", "key5"));
Assert.assertEquals("value", getInVM(vm2, "B", "key5"));
stopBridgeMemberVM(vm1);
putInVM(vm3, "B", "key6", "value");
Assert.assertEquals("value", getInVM(vm2, "B", "key6"));
vm1.invoke("Start BridgeServer", () -> startBridgeServer(new String[] {"group1", "group2"},
locators, new String[] {"A", "B"}));
stopBridgeMemberVM(vm2);
putInVM(vm3, "B", "key7", "value");
Assert.assertEquals("value", getInVM(vm1, "B", "key7"));
}
@Test
public void testTwoServersInSameVM() throws Exception {
final Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
// VM vm3 = host.getVM(3);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
vm0.invoke("Start Locator",
() -> startLocator(NetworkUtils.getServerHostName(vm0.getHost()), locatorPort, ""));
final String locators = NetworkUtils.getServerHostName(vm0.getHost()) + "[" + locatorPort + "]";
final int serverPort1 = vm1.invoke("Start BridgeServer",
() -> startBridgeServer(new String[] {"group1"}, locators));
final int serverPort2 =
vm1.invoke("Start CacheServer", () -> addCacheServer(new String[] {"group2"}));
vm2.invoke("StartBridgeClient", () -> startBridgeClient("group2",
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
checkEndpoints(vm2, new int[] {serverPort2});
stopBridgeMemberVM(vm2);
vm2.invoke("StartBridgeClient", () -> startBridgeClient("group1",
NetworkUtils.getServerHostName(vm0.getHost()), locatorPort));
checkEndpoints(vm2, new int[] {serverPort1});
}
@Test
public void testClientMembershipListener() throws Exception {
final Host host = Host.getHost(0);
VM locatorVM = host.getVM(0);
VM bridge1VM = host.getVM(1);
VM bridge2VM = host.getVM(2);
VM clientVM = host.getVM(3);
int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
locatorVM.invoke("Start Locator",
() -> startLocator(NetworkUtils.getServerHostName(locatorVM.getHost()), locatorPort, ""));
String locators = NetworkUtils.getServerHostName(locatorVM.getHost()) + "[" + locatorPort + "]";
// start a bridge server with a listener
addBridgeListener(bridge1VM);
int serverPort1 =
bridge1VM.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
// start a bridge client with a listener
addBridgeListener(clientVM);
clientVM.invoke("StartBridgeClient", () -> {
String locatorHostName = NetworkUtils.getServerHostName(locatorVM.getHost());
startBridgeClient(null, locatorHostName, locatorPort);
});
// wait for client to connect
checkEndpoints(clientVM, new int[] {serverPort1});
// make sure the client and bridge server both noticed each other
waitForJoin(bridge1VM);
MyListener serverListener = getBridgeListener(bridge1VM);
Assert.assertEquals(0, serverListener.getCrashes());
Assert.assertEquals(0, serverListener.getDepartures());
Assert.assertEquals(1, serverListener.getJoins());
resetBridgeListener(bridge1VM);
waitForJoin(clientVM);
MyListener clientListener = getBridgeListener(clientVM);
Assert.assertEquals(0, clientListener.getCrashes());
Assert.assertEquals(0, clientListener.getDepartures());
Assert.assertEquals(1, clientListener.getJoins());
resetBridgeListener(clientVM);
checkEndpoints(clientVM, new int[] {serverPort1});
// start another bridge server and make sure it is detected by the client
int serverPort2 =
bridge2VM.invoke("Start BridgeServer", () -> startBridgeServer(null, locators));
checkEndpoints(clientVM, new int[] {serverPort1, serverPort2});
serverListener = getBridgeListener(bridge1VM);
Assert.assertEquals(0, serverListener.getCrashes());
Assert.assertEquals(0, serverListener.getDepartures());
Assert.assertEquals(0, serverListener.getJoins());
resetBridgeListener(bridge1VM);
waitForJoin(clientVM);
clientListener = getBridgeListener(clientVM);
Assert.assertEquals(0, clientListener.getCrashes());
Assert.assertEquals(0, clientListener.getDepartures());
Assert.assertEquals(1, clientListener.getJoins());
resetBridgeListener(clientVM);
// stop the second bridge server and make sure it is detected by the client
stopBridgeMemberVM(bridge2VM);
checkEndpoints(clientVM, new int[] {serverPort1});
serverListener = getBridgeListener(bridge1VM);
Assert.assertEquals(0, serverListener.getCrashes());
Assert.assertEquals(0, serverListener.getDepartures());
Assert.assertEquals(0, serverListener.getJoins());
resetBridgeListener(bridge1VM);
waitForCrash(clientVM);
clientListener = getBridgeListener(clientVM);
Assert.assertEquals(0, clientListener.getJoins());
Assert.assertEquals(1, clientListener.getDepartures() + clientListener.getCrashes());
resetBridgeListener(clientVM);
// stop the client and make sure the bridge server notices
stopBridgeMemberVM(clientVM);
waitForDeparture(bridge1VM);
serverListener = getBridgeListener(bridge1VM);
Assert.assertEquals(0, serverListener.getCrashes());
Assert.assertEquals(1, serverListener.getDepartures());
Assert.assertEquals(0, serverListener.getJoins());
}
protected Object getInVM(VM vm, final Serializable key) {
return getInVM(vm, REGION_NAME, key);
}
protected Object getInVM(VM vm, final String regionName, final Serializable key) {
return vm.invoke(new SerializableCallable("Get in VM") {
public Object call() throws Exception {
Cache cache = (Cache) remoteObjects.get(CACHE_KEY);
Region region = cache.getRegion(regionName);
return region.get(key);
}
});
}
protected void putAndWaitForSuccess(VM vm, final String regionName, final Serializable key,
final Serializable value) throws InterruptedException {
long endTime = System.currentTimeMillis() + MAX_WAIT;
long remaining = MAX_WAIT;
int i = 0;
while (true) {
try {
System.err.println("Attempt: " + (i++));
putInVM(vm, regionName, key, value);
break;
} catch (NoAvailableLocatorsException | org.apache.geode.test.dunit.RMIException e) {
if (!(e instanceof NoAvailableLocatorsException)
&& !(e.getCause() instanceof NoAvailableServersException)) {
throw e;
}
if (remaining <= 0) {
throw e;
}
Wait.pause(100);
remaining = endTime - System.currentTimeMillis();
}
}
}
protected void putInVM(VM vm, final Serializable key, final Serializable value) {
putInVM(vm, REGION_NAME, key, value);
}
protected void putInVM(VM vm, final String regionName, final Serializable key,
final Serializable value) {
vm.invoke(new SerializableCallable("Put in VM") {
public Object call() throws Exception {
Cache cache = (Cache) remoteObjects.get(CACHE_KEY);
Region region = cache.getRegion(regionName);
return region.put(key, value);
}
});
}
/**
* Assert that there is one endpoint with the given host in port on the client vm.
*
* @param vm - the vm the client is running in
* @param expectedPorts - The server ports we expect the client to be connected to.
*/
protected void checkEndpoints(VM vm, final int[] expectedPorts) {
vm.invoke(new SerializableRunnable("Check endpoint") {
public void run() {
PoolImpl pool = (PoolImpl) PoolManager.find(POOL_NAME);
int retryCount = 50;
List/* <ServerLocation> */ endpoints;
HashSet actualEndpointPorts;
HashSet expectedEndpointPorts = new HashSet();
for (int i = 0; i < expectedPorts.length; i++) {
expectedEndpointPorts.add(new Integer(expectedPorts[i]));
}
do {
endpoints = pool.getCurrentServers();
actualEndpointPorts = new HashSet();
for (Iterator itr = endpoints.iterator(); itr.hasNext();) {
ServerLocation sl = (ServerLocation) itr.next();
actualEndpointPorts.add(new Integer(sl.getPort()));
}
if (expectedEndpointPorts.size() == actualEndpointPorts.size()) {
break;
}
Wait.pause(100);
} while (retryCount-- > 0);
Assert.assertEquals(expectedEndpointPorts, actualEndpointPorts);
}
});
}
protected void addBridgeListener(VM vm) {
vm.invoke(new SerializableRunnable("Add membership listener") {
public void run() {
MyListener listener = new MyListener();
ClientMembership.registerClientMembershipListener(listener);
remoteObjects.put(BRIDGE_LISTENER, listener);
}
});
}
protected void resetBridgeListener(VM vm) {
vm.invoke(new SerializableRunnable("Reset membership listener") {
public void run() {
MyListener listener = (MyListener) remoteObjects.get(BRIDGE_LISTENER);
listener.reset();
}
});
}
private MyListener getBridgeListener(VM vm) {
return (MyListener) vm.invoke(new SerializableCallable("Get membership listener") {
public Object call() {
return remoteObjects.get(BRIDGE_LISTENER);
}
});
}
private void waitForJoin(VM vm) {
vm.invoke(new SerializableRunnable("wait for join") {
public void run() {
MyListener listener = (MyListener) remoteObjects.get(BRIDGE_LISTENER);
synchronized (listener) {
long end = System.currentTimeMillis() + 10000;
while (listener.joins == 0) {
try {
long remaining = end - System.currentTimeMillis();
if (remaining <= 0) {
break;
}
listener.wait(remaining);
} catch (InterruptedException e) {
fail("interrupted");
}
}
GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
}
}
});
}
private void waitForCrash(VM vm) {
vm.invoke(new SerializableRunnable("wait for crash") {
public void run() {
MyListener listener = (MyListener) remoteObjects.get(BRIDGE_LISTENER);
synchronized (listener) {
long end = System.currentTimeMillis() + 10000;
while (listener.crashes == 0) {
try {
long remaining = end - System.currentTimeMillis();
if (remaining <= 0) {
return;
}
listener.wait(remaining);
} catch (InterruptedException e) {
fail("interrupted");
}
}
}
}
});
}
private void waitForDeparture(VM vm) {
vm.invoke(new SerializableRunnable("wait for departure") {
public void run() {
MyListener listener = (MyListener) remoteObjects.get(BRIDGE_LISTENER);
synchronized (listener) {
long end = System.currentTimeMillis() + 10000;
while (listener.departures == 0) {
try {
long remaining = end - System.currentTimeMillis();
if (remaining < 0) {
break;
}
listener.wait(remaining);
} catch (InterruptedException e) {
fail("interrupted");
}
}
}
}
});
}
public static class MyListener extends ClientMembershipListenerAdapter implements Serializable {
protected volatile int crashes = 0;
protected volatile int joins = 0;
protected volatile int departures = 0;
@Override
public synchronized void memberCrashed(ClientMembershipEvent event) {
crashes++;
System.out.println("memberCrashed invoked");
notifyAll();
}
public synchronized void reset() {
crashes = 0;
joins = 0;
departures = 0;
}
@Override
public synchronized void memberJoined(ClientMembershipEvent event) {
joins++;
System.out.println("memberJoined invoked");
notifyAll();
}
@Override
public synchronized void memberLeft(ClientMembershipEvent event) {
departures++;
System.out.println("memberLeft invoked");
notifyAll();
}
public synchronized int getCrashes() {
return crashes;
}
public synchronized int getJoins() {
return joins;
}
public synchronized int getDepartures() {
return departures;
}
}
}