/*
* 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 java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.management.cli.CliMetaData;
import org.apache.geode.management.cli.ConverterHint;
import org.apache.geode.management.cli.Result;
import org.apache.geode.management.internal.cli.CliUtil;
import org.apache.geode.management.internal.cli.domain.DurableCqNamesResult;
import org.apache.geode.management.internal.cli.domain.MemberResult;
import org.apache.geode.management.internal.cli.domain.SubscriptionQueueSizeResult;
import org.apache.geode.management.internal.cli.functions.CloseDurableClientFunction;
import org.apache.geode.management.internal.cli.functions.CloseDurableCqFunction;
import org.apache.geode.management.internal.cli.functions.GetSubscriptionQueueSizeFunction;
import org.apache.geode.management.internal.cli.functions.ListDurableCqNamesFunction;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.result.CommandResultException;
import org.apache.geode.management.internal.cli.result.ErrorResultData;
import org.apache.geode.management.internal.cli.result.InfoResultData;
import org.apache.geode.management.internal.cli.result.ResultBuilder;
import org.apache.geode.management.internal.cli.result.TabularResultData;
import org.apache.geode.management.internal.security.ResourceOperation;
import org.apache.geode.security.ResourcePermission.Operation;
import org.apache.geode.security.ResourcePermission.Resource;
import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
/**
* The DurableClientCommands class encapsulates all GemFire shell (Gfsh) commands related to durable
* clients and cqs defined in GemFire.
* </p>
*/
@SuppressWarnings("unused")
public class DurableClientCommands extends AbstractCommandsSupport {
private static final ListDurableCqNamesFunction listDurableCqNamesFunction =
new ListDurableCqNamesFunction();
private static final CloseDurableClientFunction closeDurableClientFunction =
new CloseDurableClientFunction();
private static final CloseDurableCqFunction closeDurableCqFunction = new CloseDurableCqFunction();
private static final GetSubscriptionQueueSizeFunction countDurableCqEvents =
new GetSubscriptionQueueSizeFunction();
@CliCommand(value = CliStrings.LIST_DURABLE_CQS, help = CliStrings.LIST_DURABLE_CQS__HELP)
@CliMetaData(shellOnly = false)
@ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
public Result listDurableClientCqs(
@CliOption(key = CliStrings.LIST_DURABLE_CQS__DURABLECLIENTID, mandatory = true,
help = CliStrings.LIST_DURABLE_CQS__DURABLECLIENTID__HELP) final String durableClientId,
@CliOption(key = CliStrings.LIST_DURABLE_CQS__MEMBER,
help = CliStrings.LIST_DURABLE_CQS__MEMBER__HELP,
optionContext = ConverterHint.MEMBERIDNAME) final String memberNameOrId,
@CliOption(key = CliStrings.LIST_DURABLE_CQS__GROUP,
help = CliStrings.LIST_DURABLE_CQS__GROUP__HELP,
optionContext = ConverterHint.MEMBERGROUP) final String group) {
Result result = null;
try {
boolean noResults = true;
Set<DistributedMember> targetMembers;
try {
targetMembers = CliUtil.findMembersOrThrow(group, memberNameOrId);
} catch (CommandResultException e) {
return e.getResult();
}
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(new ListDurableCqNamesFunction(), durableClientId, targetMembers);
final List<DurableCqNamesResult> results = (List<DurableCqNamesResult>) rc.getResult();
Map<String, List<String>> memberCqNamesMap = new TreeMap<String, List<String>>();
Map<String, List<String>> errorMessageNodes = new HashMap<String, List<String>>();
Map<String, List<String>> exceptionMessageNodes = new HashMap<String, List<String>>();
for (DurableCqNamesResult memberResult : results) {
if (memberResult != null) {
if (memberResult.isSuccessful()) {
memberCqNamesMap.put(memberResult.getMemberNameOrId(), memberResult.getCqNamesList());
} else {
if (memberResult.isOpPossible()) {
groupByMessage(memberResult.getExceptionMessage(), memberResult.getMemberNameOrId(),
exceptionMessageNodes);
} else {
groupByMessage(memberResult.getErrorMessage(), memberResult.getMemberNameOrId(),
errorMessageNodes);
}
}
}
}
if (!memberCqNamesMap.isEmpty()) {
TabularResultData table = ResultBuilder.createTabularResultData();
Set<String> members = memberCqNamesMap.keySet();
for (String member : members) {
boolean isFirst = true;
List<String> cqNames = memberCqNamesMap.get(member);
for (String cqName : cqNames) {
if (isFirst) {
isFirst = false;
table.accumulate(CliStrings.LIST_DURABLE_CQS__MEMBER, member);
} else {
table.accumulate(CliStrings.LIST_DURABLE_CQS__MEMBER, "");
}
table.accumulate(CliStrings.LIST_DURABLE_CQS__NAME, cqName);
}
}
result = ResultBuilder.buildResult(table);
} else {
String errorHeader =
CliStrings.format(CliStrings.LIST_DURABLE_CQS__FAILURE__HEADER, durableClientId);
result = ResultBuilder.buildResult(
buildFailureData(null, exceptionMessageNodes, errorMessageNodes, errorHeader));
}
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
return result;
}
@CliCommand(value = CliStrings.COUNT_DURABLE_CQ_EVENTS,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__HELP)
@CliMetaData(shellOnly = false)
@ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
public Result countDurableCqEvents(
@CliOption(key = CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID, mandatory = true,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CLIENT__ID__HELP) final String durableClientId,
@CliOption(key = CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CQ__NAME, mandatory = false,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__DURABLE__CQ__NAME__HELP) final String cqName,
@CliOption(key = CliStrings.COUNT_DURABLE_CQ_EVENTS__MEMBER, mandatory = false,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__MEMBER__HELP,
optionContext = ConverterHint.MEMBERIDNAME) final String memberNameOrId,
@CliOption(key = CliStrings.COUNT_DURABLE_CQ_EVENTS__GROUP, mandatory = false,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__GROUP__HELP,
optionContext = ConverterHint.MEMBERGROUP) final String group) {
Result result = null;
try {
Set<DistributedMember> targetMembers;
try {
targetMembers = CliUtil.findMembersOrThrow(group, memberNameOrId);
} catch (CommandResultException e) {
return e.getResult();
}
String[] params = new String[2];
params[0] = durableClientId;
params[1] = cqName;
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(new GetSubscriptionQueueSizeFunction(), params, targetMembers);
final List<SubscriptionQueueSizeResult> funcResults =
(List<SubscriptionQueueSizeResult>) rc.getResult();
String queueSizeColumnName;
if (cqName != null && !cqName.isEmpty()) {
queueSizeColumnName = CliStrings
.format(CliStrings.COUNT_DURABLE_CQ_EVENTS__SUBSCRIPTION__QUEUE__SIZE__CLIENT, cqName);
} else {
queueSizeColumnName = CliStrings.format(
CliStrings.COUNT_DURABLE_CQ_EVENTS__SUBSCRIPTION__QUEUE__SIZE__CLIENT, durableClientId);
}
result = buildTableResultForQueueSize(funcResults, queueSizeColumnName);
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
return result;
}
@CliCommand(value = CliStrings.CLOSE_DURABLE_CLIENTS,
help = CliStrings.CLOSE_DURABLE_CLIENTS__HELP)
@CliMetaData(shellOnly = false)
@ResourceOperation(resource = Resource.DATA, operation = Operation.MANAGE)
public Result closeDurableClient(
@CliOption(key = CliStrings.CLOSE_DURABLE_CLIENTS__CLIENT__ID, mandatory = true,
help = CliStrings.CLOSE_DURABLE_CLIENTS__CLIENT__ID__HELP) final String durableClientId,
@CliOption(key = CliStrings.CLOSE_DURABLE_CLIENTS__MEMBER, mandatory = false,
help = CliStrings.CLOSE_DURABLE_CLIENTS__MEMBER__HELP,
optionContext = ConverterHint.MEMBERIDNAME) final String memberNameOrId,
@CliOption(key = CliStrings.CLOSE_DURABLE_CLIENTS__GROUP, mandatory = false,
help = CliStrings.COUNT_DURABLE_CQ_EVENTS__GROUP__HELP,
optionContext = ConverterHint.MEMBERGROUP) final String group) {
Result result = null;
try {
Set<DistributedMember> targetMembers;
try {
targetMembers = CliUtil.findMembersOrThrow(group, memberNameOrId);
} catch (CommandResultException e) {
return e.getResult();
}
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(new CloseDurableClientFunction(), durableClientId, targetMembers);
final List<MemberResult> results = (List<MemberResult>) rc.getResult();
String failureHeader =
CliStrings.format(CliStrings.CLOSE_DURABLE_CLIENTS__FAILURE__HEADER, durableClientId);
String successHeader =
CliStrings.format(CliStrings.CLOSE_DURABLE_CLIENTS__SUCCESS, durableClientId);
result = buildResult(results, successHeader, failureHeader);
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
return result;
}
@CliCommand(value = CliStrings.CLOSE_DURABLE_CQS, help = CliStrings.CLOSE_DURABLE_CQS__HELP)
@CliMetaData(shellOnly = false)
@ResourceOperation(resource = Resource.DATA, operation = Operation.MANAGE)
public Result closeDurableCqs(@CliOption(key = CliStrings.CLOSE_DURABLE_CQS__DURABLE__CLIENT__ID,
mandatory = true,
help = CliStrings.CLOSE_DURABLE_CQS__DURABLE__CLIENT__ID__HELP) final String durableClientId,
@CliOption(key = CliStrings.CLOSE_DURABLE_CQS__NAME, mandatory = true,
help = CliStrings.CLOSE_DURABLE_CQS__NAME__HELP) final String cqName,
@CliOption(key = CliStrings.CLOSE_DURABLE_CQS__MEMBER, mandatory = false,
help = CliStrings.CLOSE_DURABLE_CQS__MEMBER__HELP,
optionContext = ConverterHint.MEMBERIDNAME) final String memberNameOrId,
@CliOption(key = CliStrings.CLOSE_DURABLE_CQS__GROUP, mandatory = false,
help = CliStrings.CLOSE_DURABLE_CQS__GROUP__HELP,
optionContext = ConverterHint.MEMBERGROUP) final String group) {
Result result = null;
try {
Set<DistributedMember> targetMembers;
try {
targetMembers = CliUtil.findMembersOrThrow(group, memberNameOrId);
} catch (CommandResultException e) {
return e.getResult();
}
String[] params = new String[2];
params[0] = durableClientId;
params[1] = cqName;
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(new CloseDurableCqFunction(), params, targetMembers);
final List<MemberResult> results = (List<MemberResult>) rc.getResult();
String failureHeader =
CliStrings.format(CliStrings.CLOSE_DURABLE_CQS__FAILURE__HEADER, cqName, durableClientId);
String successHeader =
CliStrings.format(CliStrings.CLOSE_DURABLE_CQS__SUCCESS, cqName, durableClientId);
result = buildResult(results, successHeader, failureHeader);
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
return result;
}
private Result buildResult(List<MemberResult> results, String successHeader,
String failureHeader) {
Result result = null;
boolean failure = true;
boolean partialFailure = false;
Map<String, List<String>> errorMap = new HashMap<String, List<String>>();
Map<String, List<String>> successMap = new HashMap<String, List<String>>();
Map<String, List<String>> exceptionMap = new HashMap<String, List<String>>();
/***
* Aggregate the results from the members
*/
for (MemberResult memberResult : results) {
if (memberResult.isSuccessful()) {
failure = false;
groupByMessage(memberResult.getSuccessMessage(), memberResult.getMemberNameOrId(),
successMap);
} else {
if (memberResult.isOpPossible()) {
partialFailure = true;
groupByMessage(memberResult.getExceptionMessage(), memberResult.getMemberNameOrId(),
exceptionMap);
} else {
groupByMessage(memberResult.getErrorMessage(), memberResult.getMemberNameOrId(),
errorMap);
}
}
}
if (!failure && !partialFailure) {
result = ResultBuilder.buildResult(buildSuccessData(successMap));
} else {
result = ResultBuilder
.buildResult(buildFailureData(successMap, exceptionMap, errorMap, failureHeader));
}
return result;
}
private Result buildTableResultForQueueSize(List<SubscriptionQueueSizeResult> results,
String queueSizeColumnName) {
Result result = null;
boolean failure = true;
Map<String, List<String>> failureMap = new HashMap<String, List<String>>();
Map<String, Long> memberQueueSizeTable = new TreeMap<String, Long>();
/***
* Aggregate the results from the members
*/
for (SubscriptionQueueSizeResult memberResult : results) {
if (memberResult.isSuccessful()) {
failure = false;
memberResult.getSubscriptionQueueSize();
memberQueueSizeTable.put(memberResult.getMemberNameOrId(),
memberResult.getSubscriptionQueueSize());
} else {
groupByMessage(memberResult.getErrorMessage(), memberResult.getMemberNameOrId(),
failureMap);
}
}
if (!failure) {
TabularResultData table = ResultBuilder.createTabularResultData();
Set<String> members = memberQueueSizeTable.keySet();
for (String member : members) {
long queueSize = memberQueueSizeTable.get(member);
table.accumulate(CliStrings.COUNT_DURABLE_CQ_EVENTS__MEMBER, member);
table.accumulate(queueSizeColumnName, queueSize);
}
result = ResultBuilder.buildResult(table);
} else {
ErrorResultData erd = ResultBuilder.createErrorResultData();
buildErrorResult(erd, failureMap);
result = ResultBuilder.buildResult(erd);
}
return result;
}
private void groupByMessage(String message, String memberNameOrId,
Map<String, List<String>> map) {
List<String> members = map.get(message);
if (members == null) {
members = new LinkedList<String>();
}
members.add(memberNameOrId);
map.put(message, members);
}
private InfoResultData buildSuccessData(Map<String, List<String>> successMap) {
InfoResultData ird = ResultBuilder.createInfoResultData();
Set<String> successMessages = successMap.keySet();
for (String successMessage : successMessages) {
ird.addLine(CliStrings.format(CliStrings.ACTION_SUCCCEEDED_ON_MEMBER, successMessage));
List<String> successfullMembers = successMap.get(successMessage);
int num = 0;
for (String member : successfullMembers) {
ird.addLine("" + ++num + "." + member);
}
ird.addLine("\n");
}
return ird;
}
private ErrorResultData buildFailureData(Map<String, List<String>> successMap,
Map<String, List<String>> exceptionMap, Map<String, List<String>> errorMap,
String errorHeader) {
ErrorResultData erd = ResultBuilder.createErrorResultData();
buildErrorResult(erd, successMap);
erd.addLine("\n");
erd.addLine(errorHeader);
buildErrorResult(erd, exceptionMap);
buildErrorResult(erd, errorMap);
return erd;
}
private void buildErrorResult(ErrorResultData erd, Map<String, List<String>> resultMap) {
if (resultMap != null && !resultMap.isEmpty()) {
Set<String> messages = resultMap.keySet();
for (String message : messages) {
erd.addLine("\n");
erd.addLine(message);
erd.addLine(CliStrings.OCCURRED_ON_MEMBERS);
List<String> members = resultMap.get(message);
int num = 0;
for (String member : members) {
++num;
erd.addLine("" + num + "." + member);
}
}
}
}
@CliAvailabilityIndicator({CliStrings.LIST_DURABLE_CQS, CliStrings.CLOSE_DURABLE_CLIENTS,
CliStrings.CLOSE_DURABLE_CQS, CliStrings.COUNT_DURABLE_CQ_EVENTS})
public boolean durableCommandsAvailable() {
boolean isAvailable = true;
if (CliUtil.isGfshVM()) {
isAvailable = getGfsh() != null && getGfsh().isConnectedAndReady();
}
return isAvailable;
}
}