/*
* 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.SystemFailure;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Execution;
import org.apache.geode.cache.execute.FunctionInvocationTargetException;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.internal.cache.execute.AbstractExecution;
import org.apache.geode.internal.lang.StringUtils;
import org.apache.geode.internal.security.IntegratedSecurityService;
import org.apache.geode.internal.security.SecurityService;
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.IndexDetails;
import org.apache.geode.management.internal.cli.domain.IndexDetails.IndexStatisticsDetails;
import org.apache.geode.management.internal.cli.domain.IndexInfo;
import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
import org.apache.geode.management.internal.cli.functions.CreateDefinedIndexesFunction;
import org.apache.geode.management.internal.cli.functions.CreateIndexFunction;
import org.apache.geode.management.internal.cli.functions.DestroyIndexFunction;
import org.apache.geode.management.internal.cli.functions.ListIndexFunction;
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.configuration.domain.XmlEntity;
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
/**
* The IndexCommands class encapsulates all GemFire shell (Gfsh) commands related to indexes defined
* in GemFire.
* </p>
*
* @see org.apache.geode.management.internal.cli.commands.AbstractCommandsSupport
* @see org.apache.geode.management.internal.cli.domain.IndexDetails
* @see org.apache.geode.management.internal.cli.functions.ListIndexFunction
* @since GemFire 7.0
*/
@SuppressWarnings("unused")
public class IndexCommands extends AbstractCommandsSupport {
private static final CreateIndexFunction createIndexFunction = new CreateIndexFunction();
private static final DestroyIndexFunction destroyIndexFunction = new DestroyIndexFunction();
private static final CreateDefinedIndexesFunction createDefinedIndexesFunction =
new CreateDefinedIndexesFunction();
private static final Set<IndexInfo> indexDefinitions =
Collections.synchronizedSet(new HashSet<IndexInfo>());
private SecurityService securityService = IntegratedSecurityService.getSecurityService();
@Override
protected Set<DistributedMember> getMembers(final Cache cache) {
// TODO determine what this does (as it is untested and unmockable!)
return CliUtil.getAllMembers(cache);
}
@CliCommand(value = CliStrings.LIST_INDEX, help = CliStrings.LIST_INDEX__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
@ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
public Result listIndex(@CliOption(key = CliStrings.LIST_INDEX__STATS, mandatory = false,
specifiedDefaultValue = "true", unspecifiedDefaultValue = "false",
help = CliStrings.LIST_INDEX__STATS__HELP) final boolean showStats) {
try {
return toTabularResult(getIndexListing(), showStats);
} catch (FunctionInvocationTargetException ignore) {
return ResultBuilder.createGemFireErrorResult(
CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN, CliStrings.LIST_INDEX));
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable t) {
SystemFailure.checkFailure();
getCache().getLogger().error(t);
return ResultBuilder.createGemFireErrorResult(
String.format(CliStrings.LIST_INDEX__ERROR_MESSAGE, toString(t, isDebugging())));
}
}
@SuppressWarnings("unchecked")
protected List<IndexDetails> getIndexListing() {
final Execution functionExecutor = getMembersFunctionExecutor(getMembers(getCache()));
if (functionExecutor instanceof AbstractExecution) {
((AbstractExecution) functionExecutor).setIgnoreDepartedMembers(true);
}
final ResultCollector<?, ?> resultsCollector =
functionExecutor.execute(new ListIndexFunction());
final List<?> results = (List<?>) resultsCollector.getResult();
final List<IndexDetails> indexDetailsList = new ArrayList<IndexDetails>(results.size());
for (Object result : results) {
if (result instanceof Set) { // ignore FunctionInvocationTargetExceptions and other Exceptions
indexDetailsList.addAll((Set<IndexDetails>) result);
}
}
Collections.sort(indexDetailsList);
return indexDetailsList;
}
protected Result toTabularResult(final List<IndexDetails> indexDetailsList,
final boolean showStats) {
if (!indexDetailsList.isEmpty()) {
final TabularResultData indexData = ResultBuilder.createTabularResultData();
for (final IndexDetails indexDetails : indexDetailsList) {
indexData.accumulate("Member Name", StringUtils.valueOf(indexDetails.getMemberName(), ""));
indexData.accumulate("Member ID", indexDetails.getMemberId());
indexData.accumulate("Region Path", indexDetails.getRegionPath());
indexData.accumulate("Name", indexDetails.getIndexName());
indexData.accumulate("Type", StringUtils.valueOf(indexDetails.getIndexType(), ""));
indexData.accumulate("Indexed Expression", indexDetails.getIndexedExpression());
indexData.accumulate("From Clause", indexDetails.getFromClause());
if (showStats) {
final IndexStatisticsDetailsAdapter adapter =
new IndexStatisticsDetailsAdapter(indexDetails.getIndexStatisticsDetails());
indexData.accumulate("Uses", adapter.getTotalUses());
indexData.accumulate("Updates", adapter.getNumberOfUpdates());
indexData.accumulate("Update Time", adapter.getTotalUpdateTime());
indexData.accumulate("Keys", adapter.getNumberOfKeys());
indexData.accumulate("Values", adapter.getNumberOfValues());
}
}
return ResultBuilder.buildResult(indexData);
} else {
return ResultBuilder.createInfoResult(CliStrings.LIST_INDEX__INDEXES_NOT_FOUND_MESSAGE);
}
}
@CliCommand(value = CliStrings.CREATE_INDEX, help = CliStrings.CREATE_INDEX__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
// TODO : Add optionContext for indexName
public Result createIndex(@CliOption(key = CliStrings.CREATE_INDEX__NAME, mandatory = true,
help = CliStrings.CREATE_INDEX__NAME__HELP) final String indexName,
@CliOption(key = CliStrings.CREATE_INDEX__EXPRESSION, mandatory = true,
help = CliStrings.CREATE_INDEX__EXPRESSION__HELP) final String indexedExpression,
@CliOption(key = CliStrings.CREATE_INDEX__REGION, mandatory = true,
optionContext = ConverterHint.REGIONPATH,
help = CliStrings.CREATE_INDEX__REGION__HELP) String regionPath,
@CliOption(key = CliStrings.CREATE_INDEX__MEMBER, mandatory = false,
optionContext = ConverterHint.MEMBERIDNAME,
help = CliStrings.CREATE_INDEX__MEMBER__HELP) final String memberNameOrID,
@CliOption(key = CliStrings.CREATE_INDEX__TYPE, mandatory = false,
unspecifiedDefaultValue = "range", optionContext = ConverterHint.INDEX_TYPE,
help = CliStrings.CREATE_INDEX__TYPE__HELP) final String indexType,
@CliOption(key = CliStrings.CREATE_INDEX__GROUP, mandatory = false,
optionContext = ConverterHint.MEMBERGROUP,
help = CliStrings.CREATE_INDEX__GROUP__HELP) final String group) {
Result result = null;
AtomicReference<XmlEntity> xmlEntity = new AtomicReference<>();
this.securityService.authorizeRegionManage(regionPath);
try {
final Cache cache = CacheFactory.getAnyInstance();
int idxType = IndexInfo.RANGE_INDEX;
// Index type check
if ("range".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.RANGE_INDEX;
} else if ("hash".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.HASH_INDEX;
} else if ("key".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.KEY_INDEX;
} else {
return ResultBuilder
.createUserErrorResult(CliStrings.CREATE_INDEX__INVALID__INDEX__TYPE__MESSAGE);
}
if (indexName == null || indexName.isEmpty()) {
return ResultBuilder.createUserErrorResult(CliStrings.CREATE_INDEX__INVALID__INDEX__NAME);
}
if (indexedExpression == null || indexedExpression.isEmpty()) {
return ResultBuilder.createUserErrorResult(CliStrings.CREATE_INDEX__INVALID__EXPRESSION);
}
if (StringUtils.isBlank(regionPath) || regionPath.equals(Region.SEPARATOR)) {
return ResultBuilder.createUserErrorResult(CliStrings.CREATE_INDEX__INVALID__REGIONPATH);
}
if (!regionPath.startsWith(Region.SEPARATOR)) {
regionPath = Region.SEPARATOR + regionPath;
}
IndexInfo indexInfo = new IndexInfo(indexName, indexedExpression, regionPath, idxType);
final Set<DistributedMember> targetMembers =
CliUtil.findMembersOrThrow(group, memberNameOrID);
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(createIndexFunction, indexInfo, targetMembers);
final List<Object> funcResults = (List<Object>) rc.getResult();
final Set<String> successfulMembers = new TreeSet<String>();
final Map<String, Set<String>> indexOpFailMap = new HashMap<String, Set<String>>();
for (final Object funcResult : funcResults) {
if (funcResult instanceof CliFunctionResult) {
final CliFunctionResult cliFunctionResult = (CliFunctionResult) funcResult;
if (cliFunctionResult.isSuccessful()) {
successfulMembers.add(cliFunctionResult.getMemberIdOrName());
if (xmlEntity.get() == null) {
xmlEntity.set(cliFunctionResult.getXmlEntity());
}
} else {
final String exceptionMessage = cliFunctionResult.getMessage();
Set<String> failedMembers = indexOpFailMap.get(exceptionMessage);
if (failedMembers == null) {
failedMembers = new TreeSet<String>();
}
failedMembers.add(cliFunctionResult.getMemberIdOrName());
indexOpFailMap.put(exceptionMessage, failedMembers);
}
}
}
if (!successfulMembers.isEmpty()) {
final InfoResultData infoResult = ResultBuilder.createInfoResultData();
infoResult.addLine(CliStrings.CREATE_INDEX__SUCCESS__MSG);
infoResult.addLine(CliStrings.format(CliStrings.CREATE_INDEX__NAME__MSG, indexName));
infoResult.addLine(
CliStrings.format(CliStrings.CREATE_INDEX__EXPRESSION__MSG, indexedExpression));
infoResult.addLine(CliStrings.format(CliStrings.CREATE_INDEX__REGIONPATH__MSG, regionPath));
infoResult.addLine(CliStrings.CREATE_INDEX__MEMBER__MSG);
int num = 0;
for (final String memberId : successfulMembers) {
++num;
infoResult.addLine(
CliStrings.format(CliStrings.CREATE_INDEX__NUMBER__AND__MEMBER, num, memberId));
}
result = ResultBuilder.buildResult(infoResult);
} else {
// Group members by the exception thrown.
final ErrorResultData erd = ResultBuilder.createErrorResultData();
erd.addLine(CliStrings.format(CliStrings.CREATE_INDEX__FAILURE__MSG, indexName));
final Set<String> exceptionMessages = indexOpFailMap.keySet();
for (final String exceptionMessage : exceptionMessages) {
erd.addLine(exceptionMessage);
erd.addLine(CliStrings.CREATE_INDEX__EXCEPTION__OCCURRED__ON);
final Set<String> memberIds = indexOpFailMap.get(exceptionMessage);
int num = 0;
for (final String memberId : memberIds) {
++num;
erd.addLine(
CliStrings.format(CliStrings.CREATE_INDEX__NUMBER__AND__MEMBER, num, memberId));
}
}
result = ResultBuilder.buildResult(erd);
}
} catch (CommandResultException crex) {
result = crex.getResult();
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
if (xmlEntity.get() != null) {
persistClusterConfiguration(result, () -> getSharedConfiguration()
.addXmlEntity(xmlEntity.get(), group != null ? group.split(",") : null));
}
return result;
}
@CliCommand(value = CliStrings.DESTROY_INDEX, help = CliStrings.DESTROY_INDEX__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
public Result destroyIndex(@CliOption(key = CliStrings.DESTROY_INDEX__NAME, mandatory = false,
unspecifiedDefaultValue = "",
help = CliStrings.DESTROY_INDEX__NAME__HELP) final String indexName,
@CliOption(key = CliStrings.DESTROY_INDEX__REGION, mandatory = false,
optionContext = ConverterHint.REGIONPATH,
help = CliStrings.DESTROY_INDEX__REGION__HELP) final String regionPath,
@CliOption(key = CliStrings.DESTROY_INDEX__MEMBER, mandatory = false,
optionContext = ConverterHint.MEMBERIDNAME,
help = CliStrings.DESTROY_INDEX__MEMBER__HELP) final String memberNameOrID,
@CliOption(key = CliStrings.DESTROY_INDEX__GROUP, mandatory = false,
optionContext = ConverterHint.MEMBERGROUP,
help = CliStrings.DESTROY_INDEX__GROUP__HELP) final String group) {
Result result = null;
if (StringUtils.isBlank(indexName) && StringUtils.isBlank(regionPath)
&& StringUtils.isBlank(memberNameOrID) && StringUtils.isBlank(group)) {
return ResultBuilder.createUserErrorResult(
CliStrings.format(CliStrings.PROVIDE_ATLEAST_ONE_OPTION, CliStrings.DESTROY_INDEX));
}
String regionName = null;
final Cache cache = CacheFactory.getAnyInstance();
// If a regionName is specified, then authorize data manage on the regionName, otherwise, it
// requires data manage permission on all regions
if (!StringUtils.isBlank(regionPath)) {
regionName = regionPath.startsWith("/") ? regionPath.substring(1) : regionPath;
this.securityService.authorizeRegionManage(regionName);
} else {
this.securityService.authorizeDataManage();
}
IndexInfo indexInfo = new IndexInfo(indexName, regionName);
Set<DistributedMember> targetMembers = null;
try {
targetMembers = CliUtil.findMembersOrThrow(group, memberNameOrID);
} catch (CommandResultException e) {
return e.getResult();
}
ResultCollector rc = CliUtil.executeFunction(destroyIndexFunction, indexInfo, targetMembers);
List<Object> funcResults = (List<Object>) rc.getResult();
Set<String> successfulMembers = new TreeSet<String>();
Map<String, Set<String>> indexOpFailMap = new HashMap<String, Set<String>>();
AtomicReference<XmlEntity> xmlEntity = new AtomicReference<>();
for (Object funcResult : funcResults) {
if (!(funcResult instanceof CliFunctionResult)) {
continue;
}
CliFunctionResult cliFunctionResult = (CliFunctionResult) funcResult;
if (cliFunctionResult.isSuccessful()) {
successfulMembers.add(cliFunctionResult.getMemberIdOrName());
if (xmlEntity.get() == null) {
xmlEntity.set(cliFunctionResult.getXmlEntity());
}
} else {
String exceptionMessage = cliFunctionResult.getMessage();
Set<String> failedMembers = indexOpFailMap.get(exceptionMessage);
if (failedMembers == null) {
failedMembers = new TreeSet<String>();
}
failedMembers.add(cliFunctionResult.getMemberIdOrName());
indexOpFailMap.put(exceptionMessage, failedMembers);
}
}
if (!successfulMembers.isEmpty()) {
InfoResultData infoResult = ResultBuilder.createInfoResultData();
if (!StringUtils.isBlank(indexName)) {
if (!StringUtils.isBlank(regionPath)) {
infoResult.addLine(CliStrings.format(CliStrings.DESTROY_INDEX__ON__REGION__SUCCESS__MSG,
indexName, regionPath));
} else {
infoResult.addLine(CliStrings.format(CliStrings.DESTROY_INDEX__SUCCESS__MSG, indexName));
}
} else {
if (!StringUtils.isBlank(regionPath)) {
infoResult.addLine(CliStrings
.format(CliStrings.DESTROY_INDEX__ON__REGION__ONLY__SUCCESS__MSG, regionPath));
} else {
infoResult.addLine(CliStrings.DESTROY_INDEX__ON__MEMBERS__ONLY__SUCCESS__MSG);
}
}
int num = 0;
for (String memberId : successfulMembers) {
infoResult.addLine(CliStrings.format(
CliStrings.format(CliStrings.DESTROY_INDEX__NUMBER__AND__MEMBER, ++num, memberId)));;
}
result = ResultBuilder.buildResult(infoResult);
} else {
ErrorResultData erd = ResultBuilder.createErrorResultData();
if (!StringUtils.isBlank(indexName)) {
erd.addLine(CliStrings.format(CliStrings.DESTROY_INDEX__FAILURE__MSG, indexName));
} else {
erd.addLine("Indexes could not be destroyed for following reasons");
}
Set<String> exceptionMessages = indexOpFailMap.keySet();
for (String exceptionMessage : exceptionMessages) {
erd.addLine(CliStrings.format(CliStrings.DESTROY_INDEX__REASON_MESSAGE, exceptionMessage));
erd.addLine(CliStrings.DESTROY_INDEX__EXCEPTION__OCCURRED__ON);
Set<String> memberIds = indexOpFailMap.get(exceptionMessage);
int num = 0;
for (String memberId : memberIds) {
erd.addLine(CliStrings.format(
CliStrings.format(CliStrings.DESTROY_INDEX__NUMBER__AND__MEMBER, ++num, memberId)));
}
erd.addLine("");
}
result = ResultBuilder.buildResult(erd);
}
if (xmlEntity.get() != null) {
persistClusterConfiguration(result, () -> getSharedConfiguration()
.deleteXmlEntity(xmlEntity.get(), group != null ? group.split(",") : null));
}
return result;
}
@CliCommand(value = CliStrings.DEFINE_INDEX, help = CliStrings.DEFINE_INDEX__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
// TODO : Add optionContext for indexName
public Result defineIndex(@CliOption(key = CliStrings.DEFINE_INDEX_NAME, mandatory = true,
help = CliStrings.DEFINE_INDEX__HELP) final String indexName,
@CliOption(key = CliStrings.DEFINE_INDEX__EXPRESSION, mandatory = true,
help = CliStrings.DEFINE_INDEX__EXPRESSION__HELP) final String indexedExpression,
@CliOption(key = CliStrings.DEFINE_INDEX__REGION, mandatory = true,
optionContext = ConverterHint.REGIONPATH,
help = CliStrings.DEFINE_INDEX__REGION__HELP) String regionPath,
@CliOption(key = CliStrings.DEFINE_INDEX__TYPE, mandatory = false,
unspecifiedDefaultValue = "range", optionContext = ConverterHint.INDEX_TYPE,
help = CliStrings.DEFINE_INDEX__TYPE__HELP) final String indexType) {
Result result = null;
XmlEntity xmlEntity = null;
this.securityService.authorizeRegionManage(regionPath);
int idxType = IndexInfo.RANGE_INDEX;
// Index type check
if ("range".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.RANGE_INDEX;
} else if ("hash".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.HASH_INDEX;
} else if ("key".equalsIgnoreCase(indexType)) {
idxType = IndexInfo.KEY_INDEX;
} else {
return ResultBuilder
.createUserErrorResult(CliStrings.DEFINE_INDEX__INVALID__INDEX__TYPE__MESSAGE);
}
if (indexName == null || indexName.isEmpty()) {
return ResultBuilder.createUserErrorResult(CliStrings.DEFINE_INDEX__INVALID__INDEX__NAME);
}
if (indexedExpression == null || indexedExpression.isEmpty()) {
return ResultBuilder.createUserErrorResult(CliStrings.DEFINE_INDEX__INVALID__EXPRESSION);
}
if (StringUtils.isBlank(regionPath) || regionPath.equals(Region.SEPARATOR)) {
return ResultBuilder.createUserErrorResult(CliStrings.DEFINE_INDEX__INVALID__REGIONPATH);
}
if (!regionPath.startsWith(Region.SEPARATOR)) {
regionPath = Region.SEPARATOR + regionPath;
}
IndexInfo indexInfo = new IndexInfo(indexName, indexedExpression, regionPath, idxType);
indexDefinitions.add(indexInfo);
final InfoResultData infoResult = ResultBuilder.createInfoResultData();
infoResult.addLine(CliStrings.DEFINE_INDEX__SUCCESS__MSG);
infoResult.addLine(CliStrings.format(CliStrings.DEFINE_INDEX__NAME__MSG, indexName));
infoResult
.addLine(CliStrings.format(CliStrings.DEFINE_INDEX__EXPRESSION__MSG, indexedExpression));
infoResult.addLine(CliStrings.format(CliStrings.DEFINE_INDEX__REGIONPATH__MSG, regionPath));
result = ResultBuilder.buildResult(infoResult);
return result;
}
@CliCommand(value = CliStrings.CREATE_DEFINED_INDEXES, help = CliStrings.CREATE_DEFINED__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
@ResourceOperation(resource = Resource.DATA, operation = Operation.MANAGE)
// TODO : Add optionContext for indexName
public Result createDefinedIndexes(
@CliOption(key = CliStrings.CREATE_DEFINED_INDEXES__MEMBER, mandatory = false,
optionContext = ConverterHint.MEMBERIDNAME,
help = CliStrings.CREATE_DEFINED_INDEXES__MEMBER__HELP) final String memberNameOrID,
@CliOption(key = CliStrings.CREATE_DEFINED_INDEXES__GROUP, mandatory = false,
optionContext = ConverterHint.MEMBERGROUP,
help = CliStrings.CREATE_DEFINED_INDEXES__GROUP__HELP) final String group) {
Result result = null;
AtomicReference<XmlEntity> xmlEntity = new AtomicReference<>();
if (indexDefinitions.isEmpty()) {
final InfoResultData infoResult = ResultBuilder.createInfoResultData();
infoResult.addLine(CliStrings.DEFINE_INDEX__FAILURE__MSG);
return ResultBuilder.buildResult(infoResult);
}
try {
final Cache cache = CacheFactory.getAnyInstance();
final Set<DistributedMember> targetMembers =
CliUtil.findMembersOrThrow(group, memberNameOrID);
final ResultCollector<?, ?> rc =
CliUtil.executeFunction(createDefinedIndexesFunction, indexDefinitions, targetMembers);
final List<Object> funcResults = (List<Object>) rc.getResult();
final Set<String> successfulMembers = new TreeSet<String>();
final Map<String, Set<String>> indexOpFailMap = new HashMap<String, Set<String>>();
for (final Object funcResult : funcResults) {
if (funcResult instanceof CliFunctionResult) {
final CliFunctionResult cliFunctionResult = (CliFunctionResult) funcResult;
if (cliFunctionResult.isSuccessful()) {
successfulMembers.add(cliFunctionResult.getMemberIdOrName());
if (xmlEntity.get() == null) {
xmlEntity.set(cliFunctionResult.getXmlEntity());
}
} else {
final String exceptionMessage = cliFunctionResult.getMessage();
Set<String> failedMembers = indexOpFailMap.get(exceptionMessage);
if (failedMembers == null) {
failedMembers = new TreeSet<String>();
}
failedMembers.add(cliFunctionResult.getMemberIdOrName());
indexOpFailMap.put(exceptionMessage, failedMembers);
}
}
}
if (!successfulMembers.isEmpty()) {
final InfoResultData infoResult = ResultBuilder.createInfoResultData();
infoResult.addLine(CliStrings.CREATE_DEFINED_INDEXES__SUCCESS__MSG);
int num = 0;
for (final String memberId : successfulMembers) {
++num;
infoResult.addLine(CliStrings
.format(CliStrings.CREATE_DEFINED_INDEXES__NUMBER__AND__MEMBER, num, memberId));
}
result = ResultBuilder.buildResult(infoResult);
} else {
// Group members by the exception thrown.
final ErrorResultData erd = ResultBuilder.createErrorResultData();
final Set<String> exceptionMessages = indexOpFailMap.keySet();
for (final String exceptionMessage : exceptionMessages) {
erd.addLine(exceptionMessage);
erd.addLine(CliStrings.CREATE_INDEX__EXCEPTION__OCCURRED__ON);
final Set<String> memberIds = indexOpFailMap.get(exceptionMessage);
int num = 0;
for (final String memberId : memberIds) {
++num;
erd.addLine(CliStrings.format(CliStrings.CREATE_DEFINED_INDEXES__NUMBER__AND__MEMBER,
num, memberId));
}
}
result = ResultBuilder.buildResult(erd);
}
} catch (CommandResultException crex) {
result = crex.getResult();
} catch (Exception e) {
result = ResultBuilder.createGemFireErrorResult(e.getMessage());
}
if (xmlEntity.get() != null) {
persistClusterConfiguration(result, () -> getSharedConfiguration()
.addXmlEntity(xmlEntity.get(), group != null ? group.split(",") : null));
}
return result;
}
@CliCommand(value = CliStrings.CLEAR_DEFINED_INDEXES, help = CliStrings.CLEAR_DEFINED__HELP)
@CliMetaData(shellOnly = false,
relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
@ResourceOperation(resource = Resource.DATA, operation = Operation.MANAGE)
// TODO : Add optionContext for indexName
public Result clearDefinedIndexes() {
indexDefinitions.clear();
final InfoResultData infoResult = ResultBuilder.createInfoResultData();
infoResult.addLine(CliStrings.CLEAR_DEFINED_INDEX__SUCCESS__MSG);
return ResultBuilder.buildResult(infoResult);
}
@CliAvailabilityIndicator({CliStrings.LIST_INDEX, CliStrings.CREATE_INDEX,
CliStrings.DESTROY_INDEX, CliStrings.CREATE_DEFINED_INDEXES, CliStrings.CLEAR_DEFINED_INDEXES,
CliStrings.DEFINE_INDEX})
public boolean indexCommandsAvailable() {
return (!CliUtil.isGfshVM() || (getGfsh() != null && getGfsh().isConnectedAndReady()));
}
protected static class IndexStatisticsDetailsAdapter {
private final IndexStatisticsDetails indexStatisticsDetails;
protected IndexStatisticsDetailsAdapter(final IndexStatisticsDetails indexStatisticsDetails) {
this.indexStatisticsDetails = indexStatisticsDetails;
}
public IndexStatisticsDetails getIndexStatisticsDetails() {
return indexStatisticsDetails;
}
public String getNumberOfKeys() {
return (getIndexStatisticsDetails() != null
? StringUtils.valueOf(getIndexStatisticsDetails().getNumberOfKeys(), "") : "");
}
public String getNumberOfUpdates() {
return (getIndexStatisticsDetails() != null
? StringUtils.valueOf(getIndexStatisticsDetails().getNumberOfUpdates(), "") : "");
}
public String getNumberOfValues() {
return (getIndexStatisticsDetails() != null
? StringUtils.valueOf(getIndexStatisticsDetails().getNumberOfValues(), "") : "");
}
public String getTotalUpdateTime() {
return (getIndexStatisticsDetails() != null
? StringUtils.valueOf(getIndexStatisticsDetails().getTotalUpdateTime(), "") : "");
}
public String getTotalUses() {
return (getIndexStatisticsDetails() != null
? StringUtils.valueOf(getIndexStatisticsDetails().getTotalUses(), "") : "");
}
}
}