/*
* 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 org.apache.geode.CancelCriterion;
import org.apache.geode.cache.*;
import org.apache.geode.cache.client.NoAvailableLocatorsException;
import org.apache.geode.cache.client.SubscriptionNotEnabledException;
import org.apache.geode.cache.client.internal.locator.ClientConnectionRequest;
import org.apache.geode.cache.client.internal.locator.ClientConnectionResponse;
import org.apache.geode.cache.client.internal.locator.LocatorListResponse;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.PoolStatHelper;
import org.apache.geode.distributed.internal.ServerLocation;
import org.apache.geode.distributed.internal.ClusterConfigurationService;
import org.apache.geode.distributed.internal.tcpserver.TcpClient;
import org.apache.geode.distributed.internal.tcpserver.TcpHandler;
import org.apache.geode.distributed.internal.tcpserver.TcpServer;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.internal.cache.PoolStats;
import org.apache.geode.internal.cache.tier.InternalClientMembership;
import org.apache.geode.management.membership.ClientMembershipEvent;
import org.apache.geode.management.membership.ClientMembershipListener;
import org.apache.geode.test.junit.categories.ClientServerTest;
import org.apache.geode.test.junit.categories.IntegrationTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import com.jayway.awaitility.Awaitility;
/**
*
*/
@SuppressWarnings("deprecation")
@Category({IntegrationTest.class, ClientServerTest.class})
public class AutoConnectionSourceImplJUnitTest {
private Cache cache;
private int port;
private FakeHandler handler;
private FakePool pool = new FakePool();
private AutoConnectionSourceImpl source;
private TcpServer server;
private ScheduledExecutorService background;
private PoolStats poolStats;
@Before
public void setUp() throws Exception {
Properties props = new Properties();
props.setProperty(MCAST_PORT, "0");
props.setProperty(LOCATORS, "");
DistributedSystem ds = DistributedSystem.connect(props);
cache = CacheFactory.create(ds);
poolStats = new PoolStats(ds, "pool");
port = AvailablePortHelper.getRandomAvailableTCPPort();
handler = new FakeHandler();
ArrayList responseLocators = new ArrayList();
responseLocators.add(new ServerLocation(InetAddress.getLocalHost().getHostName(), port));
handler.nextLocatorListResponse = new LocatorListResponse(responseLocators, false);
// very irritating, the SystemTimer requires having a distributed system
Properties properties = new Properties();
properties.put(MCAST_PORT, "0");
properties.put(LOCATORS, "");
background = Executors.newSingleThreadScheduledExecutor();
List/* <InetSocketAddress> */ locators = new ArrayList();
locators.add(new InetSocketAddress(InetAddress.getLocalHost(), port));
source = new AutoConnectionSourceImpl(locators, "", 60 * 1000);
source.start(pool);
}
@After
public void tearDown() {
background.shutdownNow();
try {
cache.close();
} catch (Exception e) {
// do nothing
}
try {
if (server != null && server.isAlive()) {
try {
new TcpClient().stop(InetAddress.getLocalHost(), port);
} catch (ConnectException ignore) {
// must not be running
}
server.join(60 * 1000);
}
} catch (Exception e) {
// do nothing
}
try {
InternalDistributedSystem.getAnyInstance().disconnect();
} catch (Exception e) {
// do nothing
}
}
@Test
public void testNoRespondingLocators() {
try {
source.findServer(null);
fail("Should have gotten a NoAvailableLocatorsException");
} catch (NoAvailableLocatorsException expected) {
// do nothing
}
}
@Test
public void testServerLocationUsedInListenerNotification() throws Exception {
final ClientMembershipEvent[] listenerEvents = new ClientMembershipEvent[1];
ClientMembershipListener listener = new ClientMembershipListener() {
@Override
public void memberJoined(final ClientMembershipEvent event) {
synchronized (listenerEvents) {
listenerEvents[0] = event;
}
}
@Override
public void memberLeft(final ClientMembershipEvent event) {}
@Override
public void memberCrashed(final ClientMembershipEvent event) {}
};
InternalClientMembership.registerClientMembershipListener(listener);
ServerLocation location = new ServerLocation("1.1.1.1", 0);
InternalClientMembership.notifyServerJoined(location);
Awaitility.await("wait for listener notification").atMost(10, TimeUnit.SECONDS).until(() -> {
synchronized (listenerEvents) {
return listenerEvents[0] != null;
}
});
assertEquals("1.1.1.1", listenerEvents[0].getMember().getHost());
InetAddress addr = InetAddress.getLocalHost();
location = new ServerLocation(addr.getHostAddress(), 0);
listenerEvents[0] = null;
InternalClientMembership.notifyServerJoined(location);
Awaitility.await("wait for listener notification").atMost(10, TimeUnit.SECONDS).until(() -> {
synchronized (listenerEvents) {
return listenerEvents[0] != null;
}
});
assertEquals(addr.getCanonicalHostName(), listenerEvents[0].getMember().getHost());
}
@Test
public void testNoServers() throws Exception {
startFakeLocator();
handler.nextConnectionResponse = new ClientConnectionResponse(null);
assertEquals(null, source.findServer(null));
}
@Test
public void testDiscoverServers() throws Exception {
startFakeLocator();
ServerLocation loc1 = new ServerLocation("localhost", 4423);
handler.nextConnectionResponse = new ClientConnectionResponse(loc1);
assertEquals(loc1, source.findServer(null));
}
@Test
public void testDiscoverLocators() throws Exception {
startFakeLocator();
int secondPort = AvailablePortHelper.getRandomAvailableTCPPort();
TcpServer server2 = new TcpServer(secondPort, InetAddress.getLocalHost(), null, null, handler,
new FakeHelper(), Thread.currentThread().getThreadGroup(), "tcp server");
server2.start();
try {
ArrayList locators = new ArrayList();
locators.add(new ServerLocation(InetAddress.getLocalHost().getHostName(), secondPort));
handler.nextLocatorListResponse = new LocatorListResponse(locators, false);
Thread.sleep(500);
try {
new TcpClient().stop(InetAddress.getLocalHost(), port);
} catch (ConnectException ignore) {
// must not be running
}
server.join(1000);
ServerLocation server1 = new ServerLocation("localhost", 10);
handler.nextConnectionResponse = new ClientConnectionResponse(server1);
assertEquals(server1, source.findServer(null));
} finally {
try {
new TcpClient().stop(InetAddress.getLocalHost(), secondPort);
} catch (ConnectException ignore) {
// must not be running
}
server.join(60 * 1000);
}
}
private void startFakeLocator() throws UnknownHostException, IOException, InterruptedException {
server = new TcpServer(port, InetAddress.getLocalHost(), null, null, handler, new FakeHelper(),
Thread.currentThread().getThreadGroup(), "Tcp Server");
server.start();
Thread.sleep(500);
}
protected static class FakeHandler implements TcpHandler {
protected volatile ClientConnectionResponse nextConnectionResponse;
protected volatile LocatorListResponse nextLocatorListResponse;;
public void init(TcpServer tcpServer) {}
public Object processRequest(Object request) throws IOException {
if (request instanceof ClientConnectionRequest) {
return nextConnectionResponse;
} else {
return nextLocatorListResponse;
}
}
public void shutDown() {}
public void endRequest(Object request, long startTime) {}
public void endResponse(Object request, long startTime) {}
public void restarting(DistributedSystem ds, GemFireCache cache,
ClusterConfigurationService sharedConfig) {}
}
public static class FakeHelper implements PoolStatHelper {
public void endJob() {}
public void startJob() {}
}
public class FakePool implements InternalPool {
public String getPoolOrCacheCancelInProgress() {
return null;
}
@Override
public boolean getKeepAlive() {
return false;
}
public EndpointManager getEndpointManager() {
return null;
}
public String getName() {
return null;
}
public PoolStats getStats() {
return poolStats;
}
public void destroy() {
}
public void detach() {}
public void destroy(boolean keepAlive) {
}
public boolean isDurableClient() {
return false;
}
public boolean isDestroyed() {
return false;
}
public int getFreeConnectionTimeout() {
return 0;
}
public int getLoadConditioningInterval() {
return 0;
}
public int getSocketBufferSize() {
return 0;
}
public int getReadTimeout() {
return 0;
}
public int getConnectionsPerServer() {
return 0;
}
public boolean getThreadLocalConnections() {
return false;
}
public boolean getSubscriptionEnabled() {
return false;
}
public boolean getPRSingleHopEnabled() {
return false;
}
public int getSubscriptionRedundancy() {
return 0;
}
public int getSubscriptionMessageTrackingTimeout() {
return 0;
}
public String getServerGroup() {
return "";
}
public List/* <InetSocketAddress> */ getLocators() {
return new ArrayList();
}
public List/* <InetSocketAddress> */ getServers() {
return new ArrayList();
}
public void releaseThreadLocalConnection() {}
public ConnectionStats getStats(ServerLocation location) {
return null;
}
public boolean getMultiuserAuthentication() {
return false;
}
public long getIdleTimeout() {
return 0;
}
public int getMaxConnections() {
return 0;
}
public int getMinConnections() {
return 0;
}
public long getPingInterval() {
return 100;
}
public int getStatisticInterval() {
return -1;
}
public int getRetryAttempts() {
return 0;
}
public Object execute(Op op) {
return null;
}
public Object executeOn(ServerLocation server, Op op) {
return null;
}
public Object executeOn(ServerLocation server, Op op, boolean accessed,
boolean onlyUseExistingCnx) {
return null;
}
public Object executeOnPrimary(Op op) {
return null;
}
public Map getEndpointMap() {
return null;
}
public ScheduledExecutorService getBackgroundProcessor() {
return background;
}
public Object executeOn(Connection con, Op op) {
return null;
}
public Object executeOn(Connection con, Op op, boolean timeoutFatal) {
return null;
}
public RegisterInterestTracker getRITracker() {
return null;
}
public int getSubscriptionAckInterval() {
return 0;
}
public Object executeOnQueuesAndReturnPrimaryResult(Op op) {
return null;
}
public CancelCriterion getCancelCriterion() {
return new CancelCriterion() {
public String cancelInProgress() {
return null;
}
public RuntimeException generateCancelledException(Throwable e) {
return null;
}
};
}
public void executeOnAllQueueServers(Op op)
throws NoSubscriptionServersAvailableException, SubscriptionNotEnabledException {
}
public Object execute(Op op, int retryAttempts) {
return null;
}
public QueryService getQueryService() {
return null;
}
public int getPendingEventCount() {
return 0;
}
public RegionService createAuthenticatedCacheView(Properties properties) {
return null;
}
public void setupServerAffinity(boolean allowFailover) {}
public void releaseServerAffinity() {}
public ServerLocation getServerAffinityLocation() {
return null;
}
public void setServerAffinityLocation(ServerLocation serverLocation) {}
}
}