/*
* 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.management.internal.cli.commands;
import org.apache.geode.cache.*;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.query.*;
import org.apache.geode.cache.query.data.Portfolio;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache30.CacheSerializableRunnable;
import org.apache.geode.internal.cache.DistributedRegion;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.cache.tier.sockets.CacheServerTestUtil;
import org.apache.geode.management.cli.Result.Status;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.result.CommandResult;
import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.SerializableCallable;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.test.junit.categories.FlakyTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.util.Properties;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import static org.apache.geode.test.dunit.Assert.assertTrue;
import static org.apache.geode.test.dunit.DistributedTestUtils.getDUnitLocatorPort;
import static org.apache.geode.test.dunit.LogWriterUtils.getLogWriter;
import static org.apache.geode.test.dunit.NetworkUtils.getServerHostName;
@Category(DistributedTest.class)
public class DurableClientCommandsDUnitTest extends CliCommandTestBase {
private static final long serialVersionUID = 1L;
final String regionName = "stocks";
final String cq1 = "cq1";
final String cq2 = "cq2";
final String cq3 = "cq3";
final String clientName = "dc1";
@Test
public void testListDurableClientCqs() throws Exception {
setupSystem();
setupCqs();
CommandStringBuilder csb = new CommandStringBuilder(CliStrings.LIST_DURABLE_CQS);
csb.addOption(CliStrings.LIST_DURABLE_CQS__DURABLECLIENTID, clientName);
String commandString = csb.toString();
writeToLog("Command String :\n ", commandString);
CommandResult commandResult = executeCommand(commandString);
String resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
assertTrue(resultAsString.contains(cq1));
assertTrue(resultAsString.contains(cq2));
assertTrue(resultAsString.contains(cq3));
closeCq(cq1);
closeCq(cq2);
closeCq(cq3);
csb = new CommandStringBuilder(CliStrings.LIST_DURABLE_CQS);
csb.addOption(CliStrings.LIST_DURABLE_CQS__DURABLECLIENTID, clientName);
commandString = csb.toString();
writeToLog("Command String :\n ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.ERROR.equals(commandResult.getStatus()));
String errorMessage =
CliStrings.format(CliStrings.LIST_DURABLE_CQS__NO__CQS__FOR__CLIENT, clientName);
assertTrue(resultAsString.contains(errorMessage));
}
@Test
public void testCloseDurableClients() throws Exception {
setupSystem();
setupCqs();
closeDurableClient();
CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CLOSE_DURABLE_CLIENTS);
csb.addOption(CliStrings.CLOSE_DURABLE_CLIENTS__CLIENT__ID, clientName);
String commandString = csb.toString();
long giveUpTime = System.currentTimeMillis() + 20000;
CommandResult commandResult = null;
String resultAsString = null;
do {
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
} while (resultAsString.contains("Cannot close a running durable client")
&& giveUpTime > System.currentTimeMillis());
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
// Execute again to see the error condition
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.ERROR.equals(commandResult.getStatus()));
String errorMessage = CliStrings.format(CliStrings.NO_CLIENT_FOUND_WITH_CLIENT_ID, clientName);
assertTrue(resultAsString.contains(errorMessage));
}
@Category(FlakyTest.class) // GEODE-1705
@Test
public void testCloseDurableCQ() throws Exception {
setupSystem();
setupCqs();
closeDurableClient();
CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CLOSE_DURABLE_CQS);
csb.addOption(CliStrings.CLOSE_DURABLE_CQS__DURABLE__CLIENT__ID, clientName);
csb.addOption(CliStrings.CLOSE_DURABLE_CQS__NAME, cq1);
String commandString = csb.toString();
writeToLog("Command String : ", commandString);
CommandResult commandResult = executeCommand(commandString);
String resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
csb = new CommandStringBuilder(CliStrings.CLOSE_DURABLE_CQS);
csb.addOption(CliStrings.CLOSE_DURABLE_CQS__DURABLE__CLIENT__ID, clientName);
csb.addOption(CliStrings.CLOSE_DURABLE_CQS__NAME, cq1);
commandString = csb.toString();
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result : ", resultAsString);
assertTrue(Status.ERROR.equals(commandResult.getStatus()));
}
@Test
public void testCountSubscriptionQueueSize() throws Exception {
setupSystem();
setupCqs();
doPuts(regionName, Host.getHost(0).getVM(1));
CommandStringBuilder csb = new CommandStringBuilder(CliStrings.COUNT_DURABLE_CQ_EVENTS);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, clientName);
String commandString = csb.toString();
writeToLog("Command String : ", commandString);
CommandResult commandResult = executeCommand(commandString);
String resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
assertTrue(resultAsString.contains("4"));
csb = new CommandStringBuilder(CliStrings.COUNT_DURABLE_CQ_EVENTS);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, clientName);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CQ__NAME, cq3);
commandString = csb.toString();
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
// CLOSE all the cqs
closeCq(cq1);
closeCq(cq2);
closeCq(cq3);
// Run the commands again
csb = new CommandStringBuilder(CliStrings.COUNT_DURABLE_CQ_EVENTS);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, clientName);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CQ__NAME, cq1);
commandString = csb.toString();
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.ERROR.equals(commandResult.getStatus()));
String errorMessage = CliStrings
.format(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE_CQ_NOT_FOUND, clientName, cq1);
assertTrue(resultAsString.contains(errorMessage));
csb = new CommandStringBuilder(CliStrings.COUNT_DURABLE_CQ_EVENTS);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, clientName);
commandString = csb.toString();
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.OK.equals(commandResult.getStatus()));
// Disconnect the client
closeDurableClient();
// Close the client
csb = new CommandStringBuilder(CliStrings.CLOSE_DURABLE_CLIENTS);
csb.addOption(CliStrings.CLOSE_DURABLE_CLIENTS__CLIENT__ID, clientName);
commandString = csb.toString();
// since it can take the server a bit to know that the client has disconnected
// we loop here
long giveUpTime = System.currentTimeMillis() + 20000;
do {
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
} while (resultAsString.contains("Cannot close a running durable client")
&& giveUpTime > System.currentTimeMillis());
writeToLog("Command Result :\n", resultAsString);
assertTrue("failed executing" + commandString + "; result = " + resultAsString,
Status.OK.equals(commandResult.getStatus()));
csb = new CommandStringBuilder(CliStrings.COUNT_DURABLE_CQ_EVENTS);
csb.addOption(CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, clientName);
commandString = csb.toString();
writeToLog("Command String : ", commandString);
commandResult = executeCommand(commandString);
resultAsString = commandResultToString(commandResult);
writeToLog("Command Result :\n", resultAsString);
assertTrue(Status.ERROR.equals(commandResult.getStatus()));
assertTrue(resultAsString
.contains(CliStrings.format(CliStrings.NO_CLIENT_FOUND_WITH_CLIENT_ID, clientName)));
}
private void writeToLog(String text, String resultAsString) {
getLogWriter().info(getUniqueName() + ": " + text + "\n" + resultAsString);
}
private void setupSystem() throws Exception {
disconnectAllFromDS();
setUpJmxManagerOnVm0ThenConnect(getServerProperties());
final VM manager = Host.getHost(0).getVM(0);
final VM server1 = Host.getHost(0).getVM(1);
final VM client1 = Host.getHost(0).getVM(2);
int listeningPort = startCacheServer(server1, 0, false, regionName);
startDurableClient(client1, server1, listeningPort, clientName, "300");
}
/**
* Close the cq from the client-side
*
* @param cqName , Name of the cq which is to be close.
*/
private void closeCq(final String cqName) {
final VM vm2 = Host.getHost(0).getVM(2);
vm2.invoke(new SerializableCallable() {
public Object call() {
QueryService qs = getCache().getQueryService();
CqAttributesFactory cqAf = new CqAttributesFactory();
try {
qs.getCq(cqName).close();
} catch (CqException e) {
e.printStackTrace();
return false;
}
return true;
}
});
}
private void setupCqs() {
final VM vm2 = Host.getHost(0).getVM(2);
vm2.invoke(new SerializableCallable() {
public Object call() {
QueryService qs = getCache().getQueryService();
CqAttributesFactory cqAf = new CqAttributesFactory();
try {
qs.newCq(cq1, "select * from /" + regionName, cqAf.create(), true).execute();
qs.newCq(cq2, "select * from /" + regionName + " where id = 1", cqAf.create(), true)
.execute();
qs.newCq(cq3, "select * from /" + regionName + " where id > 2", cqAf.create(), true)
.execute();
} catch (CqException e) {
e.printStackTrace();
return false;
} catch (CqExistsException e) {
e.printStackTrace();
return false;
} catch (RegionNotFoundException e) {
e.printStackTrace();
return false;
}
return true;
}
});
}
private int startCacheServer(VM server, final int port, final boolean createPR,
final String regionName) throws Exception {
Integer listeningPort = (Integer) server.invoke(new SerializableCallable() {
public Object call() throws Exception {
getSystem(getServerProperties());
GemFireCacheImpl cache = (GemFireCacheImpl) getCache();
AttributesFactory factory = new AttributesFactory();
if (createPR) {
PartitionAttributesFactory paf = new PartitionAttributesFactory();
paf.setRedundantCopies(1);
paf.setTotalNumBuckets(11);
factory.setPartitionAttributes(paf.create());
} else {
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setDataPolicy(DataPolicy.REPLICATE);
}
Region region = createRootRegion(regionName, factory.create());
if (createPR) {
assertTrue(region instanceof PartitionedRegion);
} else {
assertTrue(region instanceof DistributedRegion);
}
CacheServer cacheServer = getCache().addCacheServer();
cacheServer.setPort(port);
cacheServer.start();
return cacheServer.getPort();
}
});
return listeningPort.intValue();
}
private void startDurableClient(VM client, final VM server, final int port,
final String durableClientId, final String durableClientTimeout) {
client.invoke(new CacheSerializableRunnable("Start client") {
public void run2() throws CacheException {
Properties props = getClientProps(durableClientId, durableClientTimeout);
getSystem(props);
final ClientCacheFactory ccf = new ClientCacheFactory(props);
ccf.addPoolServer(getServerHostName(server.getHost()), port);
ccf.setPoolSubscriptionEnabled(true);
ClientCache cache = (ClientCache) getClientCache(ccf);
}
});
}
/**
* Does few puts on the region on the server
*/
private void doPuts(final String regionName, VM server) {
server.invoke(new SerializableCallable() {
public Object call() throws Exception {
GemFireCacheImpl cache = (GemFireCacheImpl) getCache();
Region region = cache.getRegion(regionName);
Portfolio p1 = new Portfolio();
p1.ID = 1;
p1.names = new String[] {"AAPL", "VMW"};
Portfolio p2 = new Portfolio();
p2.ID = 2;
p2.names = new String[] {"EMC", "IBM"};
Portfolio p3 = new Portfolio();
p3.ID = 5;
p3.names = new String[] {"DOW", "TON"};
Portfolio p4 = new Portfolio();
p4.ID = 5;
p4.names = new String[] {"ABC", "EBAY"};
region.put("p1", p1);
region.put("p2", p2);
region.put("p3", p3);
region.put("p4", p4);
return null;
}
});
}
// Closes the durable-client from the client side.
private void closeDurableClient() {
final VM client = Host.getHost(0).getVM(2);
client.invoke(new CacheSerializableRunnable("Stop client") {
public void run2() throws CacheException {
ClientCacheFactory.getAnyInstance().close(true);
}
});
}
protected Properties getClientProps(String durableClientId, String durableClientTimeout) {
Properties p = new Properties();
p.setProperty(MCAST_PORT, "0");
p.setProperty(LOCATORS, "");
p.setProperty(DURABLE_CLIENT_ID, durableClientId);
p.setProperty(DURABLE_CLIENT_TIMEOUT, String.valueOf(durableClientTimeout));
return p;
}
protected Properties getServerProperties() {
Properties p = new Properties();
p.setProperty(LOCATORS, "localhost[" + getDUnitLocatorPort() + "]");
return p;
}
@Override
public final void postTearDownCacheTestCase() throws Exception {
Host.getHost(0).getVM(0).invoke(() -> CacheServerTestUtil.closeCache());
Host.getHost(0).getVM(1).invoke(() -> CacheServerTestUtil.closeCache());
Host.getHost(0).getVM(2).invoke(() -> CacheServerTestUtil.closeCache());
}
}