/*
* 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.internal.cache.tier.sockets;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import static org.junit.Assert.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.apache.geode.test.junit.categories.ClientServerTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.internal.AvailablePort;
import org.apache.geode.internal.cache.CacheServerImpl;
import org.apache.geode.internal.cache.ha.HARegionQueue;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.LogWriterUtils;
import org.apache.geode.test.dunit.NetworkUtils;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.DistributedTest;
/**
* This tests the fix for bug 73210. Reason for the bug was that HARegionQueue's destroy was not
* being called on CacheClientProxy's closure. As a result, stats were left open.
*/
@Category({DistributedTest.class, ClientServerTest.class})
public class Bug37210DUnitTest extends JUnit4DistributedTestCase {
/** the cache server */
VM server = null;
/** the cache client */
VM client = null;
/** the cache */
private static Cache cache = null;
/** port for the cache server */
private int PORT;
/** name of the test region */
private static final String REGION_NAME = "Bug37210DUnitTest_Region";
/**
* Creates the cache server and sets the port
*
* @throws Exception - thrown if any problem occurs in initializing the test
*/
@Override
public final void postSetUp() throws Exception {
IgnoredException.addIgnoredException("java.io.IOException");
final Host host = Host.getHost(0);
server = host.getVM(0);
client = host.getVM(2);
PORT = ((Integer) server.invoke(() -> Bug37210DUnitTest.createServerCache())).intValue();
}
/**
* Create the cache
*
* @param props - properties for DS
* @return the cache instance
* @throws Exception - thrown if any problem occurs in cache creation
*/
private Cache createCache(Properties props) throws Exception {
DistributedSystem ds = getSystem(props);
ds.disconnect();
ds = getSystem(props);
Cache cache = null;
cache = CacheFactory.create(ds);
if (cache == null) {
throw new Exception("CacheFactory.create() returned null ");
}
return cache;
}
/**
* close the cache instances in server and client during tearDown
*
* @throws Exception thrown if any problem occurs in closing cache
*/
@Override
public final void preTearDown() throws Exception {
// close client
client.invoke(() -> Bug37210DUnitTest.closeCache());
// close server
server.invoke(() -> Bug37210DUnitTest.closeCache());
}
/**
* This test does the following:<br>
* 1)Create the client<br>
* 2)Do some operations from the cache-server<br>
* 3)Stop the primary cache-server<br>
* 4)Explicity close the CacheClientProxy on the server. <br>
* 5)Verify that HARegionQueue stats are closed and entry for the haregion is removed from
* dispatchedMessagesMap.
*
* @throws Exception - thrown if any problem occurs in test execution
*/
@Test
public void testHAStatsCleanup() throws Exception {
Host host = Host.getHost(0);
LogWriterUtils.getLogWriter().info("testHAStatsCleanup : BEGIN");
IgnoredException.addIgnoredException("java.net.SocketException");
IgnoredException.addIgnoredException("Unexpected IOException");
client.invoke(() -> Bug37210DUnitTest.createClientCache(NetworkUtils.getServerHostName(host),
new Integer(PORT)));
server.invoke(() -> Bug37210DUnitTest.doEntryOperations());
server.invoke(() -> Bug37210DUnitTest.closeCacheClientProxyAndVerifyStats());
client.invoke(() -> Bug37210DUnitTest.closeCache());
// we don't send close response thus needs to wait for client termination
Thread.currentThread().sleep(1000);
server.invoke(() -> Bug37210DUnitTest.closeCacheClientProxyAndVerifyStats2());
LogWriterUtils.getLogWriter().info("testHAStatsCleanup : END");
}
/**
* Creates and starts the cache-server
*
* @return - the port on which cache-server is running
* @throws Exception - thrown if any problem occurs in cache/server creation
*/
public static Integer createServerCache() throws Exception {
Bug37210DUnitTest test = new Bug37210DUnitTest();
Properties props = new Properties();
cache = test.createCache(props);
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setDataPolicy(DataPolicy.REPLICATE);
RegionAttributes attrs = factory.create();
cache.createRegion(REGION_NAME, attrs);
CacheServer server = cache.addCacheServer();
int port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
server.setPort(port);
server.setNotifyBySubscription(false);
server.setSocketBufferSize(32768);
server.setMaximumTimeBetweenPings(1000000);
server.start();
LogWriterUtils.getLogWriter().info("Server started at PORT = " + port);
return new Integer(server.getPort());
}
/**
* Initializes the cache client
*
* @param port - port for the primary cache-server
*
* @throws Exception-thrown if any problem occurs in initializing the client
*/
public static void createClientCache(String host, Integer port) throws Exception {
Bug37210DUnitTest test = new Bug37210DUnitTest();
Properties props = new Properties();
props.setProperty(MCAST_PORT, "0");
props.setProperty(LOCATORS, "");
cache = test.createCache(props);
Pool p = PoolManager.createFactory().addServer(host, port.intValue())
.setSubscriptionEnabled(true).setThreadLocalConnections(true).setReadTimeout(10000)
.setSocketBufferSize(32768).setMinConnections(3).setSubscriptionRedundancy(-1)
// .setRetryInterval(10000)
// .setRetryAttempts(5)
.create("Bug37210UnitTestPool");
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setPoolName(p.getName());
RegionAttributes attrs = factory.create();
Region region = cache.createRegion(REGION_NAME, attrs);
region.registerInterest("ALL_KEYS");
}
/**
* Close the CacheClientProxy of the client on the server and verify that ha-stats are closed and
* the entry for the region is removed from dispatchedMessagesMap.
*
*/
public static void closeCacheClientProxyAndVerifyStats() {
assertEquals("More than one BridgeServers found ", 1, cache.getCacheServers().size());
CacheServerImpl bs = (CacheServerImpl) cache.getCacheServers().iterator().next();
assertNotNull(bs);
assertNotNull(bs.getAcceptor());
assertNotNull(bs.getAcceptor().getCacheClientNotifier());
Iterator proxies = bs.getAcceptor().getCacheClientNotifier().getClientProxies().iterator();
assertTrue("No proxy found", proxies.hasNext());
CacheClientProxy proxy = (CacheClientProxy) proxies.next();
Map dispatchedMsgMap = HARegionQueue.getDispatchedMessagesMapForTesting();
HARegionQueue rq = proxy.getHARegionQueue();
Object value = dispatchedMsgMap.get(rq.getRegion().getName());
proxy.close();
assertTrue("HARegionQueue stats were not closed on proxy.close()",
rq.getStatistics().isClosed());
}
public static void closeCacheClientProxyAndVerifyStats2() {
Map dispatchedMsgMap = HARegionQueue.getDispatchedMessagesMapForTesting();
assertTrue(
"HARegionQueue.dispatchedMessagesMap contains entry for the region even after proxy.close()",
dispatchedMsgMap.size() == 0);
}
/**
* Do some PUT operations
*
* @throws Exception - thrown if any exception occurs in doing PUTs
*/
public static void doEntryOperations() throws Exception {
Region r1 = cache.getRegion(Region.SEPARATOR + REGION_NAME);
String keyPrefix = "server-";
for (int i = 0; i < 10; i++) {
r1.put(keyPrefix + i, keyPrefix + "val-" + i);
}
}
/**
* Close the cache
*
*/
public static void closeCache() {
if (cache != null && !cache.isClosed()) {
cache.close();
cache.getDistributedSystem().disconnect();
}
}
}