You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2018/05/24 20:31:20 UTC

[geode] branch develop updated: GEODE-4858: Convert *DefinedIndex commands to use ResultModel and Sin… (#1983)

This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 71145b6  GEODE-4858: Convert *DefinedIndex commands to use ResultModel and Sin… (#1983)
71145b6 is described below

commit 71145b6f09efe5dbf8627a0525acef33777b8bed
Author: Jens Deppe <jd...@pivotal.io>
AuthorDate: Thu May 24 13:31:13 2018 -0700

    GEODE-4858: Convert *DefinedIndex commands to use ResultModel and Sin… (#1983)
---
 .../geode/cache/configuration/RegionConfig.java    |  10 +-
 .../org/apache/geode/cache/query/IndexType.java    |  12 ++
 .../cli/commands/ClearDefinedIndexesCommand.java   |  11 +-
 .../cli/commands/CreateDefinedIndexesCommand.java  | 136 +++++-------------
 .../internal/cli/commands/DefineIndexCommand.java  |  22 +--
 .../internal/cli/commands/IndexDefinition.java     |  17 ++-
 .../management/internal/cli/domain/IndexInfo.java  | 115 ---------------
 .../functions/CreateDefinedIndexesFunction.java    | 106 ++++++--------
 .../internal/cli/remote/CommandExecutor.java       |   2 +-
 .../internal/cli/result/model/ResultModel.java     |   3 +
 .../sanctioned-geode-core-serializables.txt        |   3 +-
 .../CreateDefinedIndexesCommandDUnitTest.java      | 155 ++++++++++++++++-----
 .../commands/CreateDefinedIndexesCommandTest.java  |  60 +++-----
 .../cli/commands/CreateJndiBindingCommandTest.java |   2 +-
 .../commands/DestroyJndiBindingCommandTest.java    |   2 +-
 .../CreateDefinedIndexesFunctionTest.java          |  85 ++++++-----
 16 files changed, 326 insertions(+), 415 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java b/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
index 447e88f..8dbb387 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
@@ -681,18 +681,24 @@ public class RegionConfig implements CacheElement {
     }
 
     /**
-     * Sets the value of the type property.
+     * Sets the value of the type property. Also sets the keyIndex property to true if the type
+     * being set is "key".
      *
      * allowed object is
      * {@link String }
      *
+     * @deprecated Index should only be a "key" or "range" type which is set using
+     *             {@link #setKeyIndex(Boolean)}
      */
     public void setType(String value) {
-      if ("range".equalsIgnoreCase(value) || "hash".equalsIgnoreCase(value)) {
+      if ("range".equalsIgnoreCase(value) || "hash".equalsIgnoreCase(value)
+          || "key".equalsIgnoreCase(value)) {
         this.type = value.toLowerCase();
       } else {
         throw new IllegalArgumentException("Invalid index type " + value);
       }
+
+      setKeyIndex("key".equalsIgnoreCase(value));
     }
 
     @Override
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/IndexType.java b/geode-core/src/main/java/org/apache/geode/cache/query/IndexType.java
index 702eca7..ce90db8 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/IndexType.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/IndexType.java
@@ -84,4 +84,16 @@ public enum IndexType {
     return name;
   }
 
+  public static IndexType valueOfSynonym(String name) {
+    name = name.toUpperCase();
+
+    switch (name) {
+      case "KEY":
+        return valueOf("PRIMARY_KEY");
+      case "RANGE":
+        return valueOf("FUNCTIONAL");
+    }
+
+    return valueOf(name);
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ClearDefinedIndexesCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ClearDefinedIndexesCommand.java
index d36bcea..5c27bd3 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ClearDefinedIndexesCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ClearDefinedIndexesCommand.java
@@ -18,10 +18,8 @@ package org.apache.geode.management.internal.cli.commands;
 import org.springframework.shell.core.annotation.CliCommand;
 
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-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.model.ResultModel;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -30,11 +28,8 @@ public class ClearDefinedIndexesCommand extends InternalGfshCommand {
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
-  // TODO : Add optionContext for indexName
-  public Result clearDefinedIndexes() {
+  public ResultModel clearDefinedIndexes() {
     IndexDefinition.indexDefinitions.clear();
-    InfoResultData infoResult = ResultBuilder.createInfoResultData();
-    infoResult.addLine(CliStrings.CLEAR_DEFINED_INDEX__SUCCESS__MSG);
-    return ResultBuilder.buildResult(infoResult);
+    return ResultModel.createInfo(CliStrings.CLEAR_DEFINED_INDEX__SUCCESS__MSG);
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommand.java
index dd00505..10816bd 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommand.java
@@ -15,33 +15,27 @@
 
 package org.apache.geode.management.internal.cli.commands;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
 
 import org.springframework.shell.core.annotation.CliCommand;
 import org.springframework.shell.core.annotation.CliOption;
 
-import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.distributed.DistributedMember;
-import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
 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.cli.SingleGfshCommand;
 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.i18n.CliStrings;
-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.configuration.domain.XmlEntity;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
-public class CreateDefinedIndexesCommand extends InternalGfshCommand {
+public class CreateDefinedIndexesCommand extends SingleGfshCommand {
+  public static final String CREATE_DEFINED_INDEXES_SECTION = "create-defined-indexes";
   private static final CreateDefinedIndexesFunction createDefinedIndexesFunction =
       new CreateDefinedIndexesFunction();
 
@@ -49,8 +43,7 @@ public class CreateDefinedIndexesCommand extends InternalGfshCommand {
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
-  // TODO : Add optionContext for indexName
-  public Result createDefinedIndexes(
+  public ResultModel createDefinedIndexes(
 
       @CliOption(key = {CliStrings.MEMBER, CliStrings.MEMBERS},
           optionContext = ConverterHint.MEMBERIDNAME,
@@ -58,106 +51,41 @@ public class CreateDefinedIndexesCommand extends InternalGfshCommand {
 
       @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
           optionContext = ConverterHint.MEMBERGROUP,
-          help = CliStrings.CREATE_DEFINED_INDEXES__GROUP__HELP) final String[] group) {
+          help = CliStrings.CREATE_DEFINED_INDEXES__GROUP__HELP) final String[] groups) {
 
-    Result result;
-    List<XmlEntity> xmlEntities = new ArrayList<>();
+    ResultModel result = new ResultModel();
 
     if (IndexDefinition.indexDefinitions.isEmpty()) {
-      final InfoResultData infoResult = ResultBuilder.createInfoResultData();
-      infoResult.addLine(CliStrings.DEFINE_INDEX__FAILURE__MSG);
-      return ResultBuilder.buildResult(infoResult);
+      return ResultModel.createInfo(CliStrings.DEFINE_INDEX__FAILURE__MSG);
     }
 
-    try {
-      final Set<DistributedMember> targetMembers = findMembers(group, memberNameOrID);
+    Set<DistributedMember> targetMembers = findMembers(groups, memberNameOrID);
+    if (targetMembers.isEmpty()) {
+      return ResultModel.createError(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
+    }
 
-      if (targetMembers.isEmpty()) {
-        return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
-      }
+    List<CliFunctionResult> functionResults = executeAndGetFunctionResult(
+        createDefinedIndexesFunction, IndexDefinition.indexDefinitions, targetMembers);
+    result.addTableAndSetStatus(CREATE_DEFINED_INDEXES_SECTION, functionResults, false);
+    result.setConfigObject(IndexDefinition.indexDefinitions);
 
-      final ResultCollector<?, ?> rc = executeFunction(createDefinedIndexesFunction,
-          IndexDefinition.indexDefinitions, targetMembers);
-
-      final List<Object> funcResults = (List<Object>) rc.getResult();
-      final Set<String> successfulMembers = new TreeSet<>();
-      final Map<String, Set<String>> indexOpFailMap = new HashMap<>();
-
-      for (final Object funcResult : funcResults) {
-        if (funcResult instanceof CliFunctionResult) {
-          final CliFunctionResult cliFunctionResult = (CliFunctionResult) funcResult;
-
-          if (cliFunctionResult.isSuccessful()) {
-            successfulMembers.add(cliFunctionResult.getMemberIdOrName());
-
-            // Only add the XmlEntity if it wasn't previously added from the result of another
-            // successful member.
-            XmlEntity resultEntity = cliFunctionResult.getXmlEntity();
-            if ((null != resultEntity) && (!xmlEntities.contains(resultEntity))) {
-              xmlEntities.add(cliFunctionResult.getXmlEntity());
-            }
-          } else {
-            final String exceptionMessage = cliFunctionResult.getMessage();
-            Set<String> failedMembers = indexOpFailMap.get(exceptionMessage);
-
-            if (failedMembers == null) {
-              failedMembers = new TreeSet<>();
-            }
-
-            failedMembers.add(cliFunctionResult.getMemberIdOrName());
-            indexOpFailMap.put(exceptionMessage, failedMembers);
-          }
-        }
-      }
+    return result;
+  }
 
-      // TODO: GEODE-3916.
-      // The index creation might succeed in some members and fail in others, the current logic only
-      // reports to the user the members on which the operation was successful, giving no details
-      // about the failures. We should report the exact details of what failed/succeeded, and
-      // where/why.
-      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 (Exception e) {
-      result = ResultBuilder.createGemFireErrorResult(e.getMessage());
+  @Override
+  public void updateClusterConfig(String group, CacheConfig config, Object configObject) {
+    Set<RegionConfig.Index> updatedIndexes = (Set<RegionConfig.Index>) configObject;
+    if (updatedIndexes == null) {
+      return;
     }
 
-    for (XmlEntity xmlEntity : xmlEntities) {
-      persistClusterConfiguration(result,
-          () -> ((InternalConfigurationPersistenceService) getConfigurationPersistenceService())
-              .addXmlEntity(xmlEntity, group));
-    }
+    for (RegionConfig.Index index : updatedIndexes) {
+      RegionConfig region = config.findRegionConfiguration(index.getFromClause());
+      if (region == null) {
+        throw new IllegalStateException("RegionConfig is null");
+      }
 
-    return result;
+      region.getIndexes().add(index);
+    }
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DefineIndexCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DefineIndexCommand.java
index 9437278..71782f3 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DefineIndexCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DefineIndexCommand.java
@@ -18,24 +18,22 @@ package org.apache.geode.management.internal.cli.commands;
 import org.springframework.shell.core.annotation.CliCommand;
 import org.springframework.shell.core.annotation.CliOption;
 
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.query.IndexType;
 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.domain.IndexInfo;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-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.model.InfoResultModel;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
 public class DefineIndexCommand extends InternalGfshCommand {
   @CliCommand(value = CliStrings.DEFINE_INDEX, help = CliStrings.DEFINE_INDEX__HELP)
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
-  // TODO : Add optionContext for indexName
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
-  public Result defineIndex(
+  public ResultModel 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,
@@ -47,18 +45,22 @@ public class DefineIndexCommand extends InternalGfshCommand {
           optionContext = ConverterHint.INDEX_TYPE,
           help = CliStrings.DEFINE_INDEX__TYPE__HELP) final IndexType indexType) {
 
-    Result result;
+    ResultModel result = new ResultModel();
+
+    RegionConfig.Index indexInfo = new RegionConfig.Index();
+    indexInfo.setName(indexName);
+    indexInfo.setExpression(indexedExpression);
+    indexInfo.setFromClause(regionPath);
+    indexInfo.setType(indexType.getName());
 
-    IndexInfo indexInfo = new IndexInfo(indexName, indexedExpression, regionPath, indexType);
     IndexDefinition.indexDefinitions.add(indexInfo);
 
-    final InfoResultData infoResult = ResultBuilder.createInfoResultData();
+    InfoResultModel infoResult = result.addInfo();
     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;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/IndexDefinition.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/IndexDefinition.java
index 2cf9f83..1f607c5 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/IndexDefinition.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/IndexDefinition.java
@@ -19,9 +19,20 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-import org.apache.geode.management.internal.cli.domain.IndexInfo;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.cache.query.IndexType;
 
 class IndexDefinition {
-  static final Set<IndexInfo> indexDefinitions =
-      Collections.synchronizedSet(new HashSet<IndexInfo>());
+  static final Set<RegionConfig.Index> indexDefinitions =
+      Collections.synchronizedSet(new HashSet<>());
+
+  static void addIndex(String name, String expression, String fromClause, IndexType type) {
+    RegionConfig.Index index = new RegionConfig.Index();
+    index.setName(name);
+    index.setFromClause(fromClause);
+    index.setType(type.getName());
+    index.setExpression(expression);
+    indexDefinitions.add(index);
+
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexInfo.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexInfo.java
deleted file mode 100644
index e353164..0000000
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexInfo.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.domain;
-
-import java.io.Serializable;
-
-import org.apache.geode.cache.query.IndexType;
-
-/***
- * Data class used to pass index related information to functions that create or destroy indexes
- *
- */
-public class IndexInfo implements Serializable {
-
-  private static final long serialVersionUID = 1L;
-
-  private String indexName;
-  private String indexedExpression = null;
-  private String regionPath = null;
-  private IndexType indexType = IndexType.FUNCTIONAL;
-  private boolean ifExists;
-
-  public IndexInfo(String indexName) {
-    this.indexName = indexName;
-  }
-
-  /***
-   * Used for passing index information for destroying index.
-   *
-   */
-  public IndexInfo(String indexName, String regionPath) {
-    this.indexName = indexName;
-    this.regionPath = regionPath;
-  }
-
-  public IndexInfo(String indexName, String indexedExpression, String regionPath) {
-    this.indexName = indexName;
-    this.indexedExpression = indexedExpression;
-    this.regionPath = regionPath;
-  }
-
-  public IndexInfo(String indexName, String indexedExpression, String regionPath,
-      IndexType indexType) {
-    this.indexName = indexName;
-    this.indexedExpression = indexedExpression;
-    this.regionPath = regionPath;
-    this.indexType = indexType;
-  }
-
-  public String getIndexName() {
-    return indexName;
-  }
-
-  public void setIndexName(String indexName) {
-    this.indexName = indexName;
-  }
-
-  public String getIndexedExpression() {
-    return indexedExpression;
-  }
-
-  public void setIndexedExpression(String indexedExpression) {
-    this.indexedExpression = indexedExpression;
-  }
-
-  public String getRegionPath() {
-    return this.regionPath;
-  }
-
-  public void setRegionPath(String regionPath) {
-    this.regionPath = regionPath;
-  }
-
-  public IndexType getIndexType() {
-    return indexType;
-  }
-
-  public void setIndexType(IndexType indexType) {
-    this.indexType = indexType;
-  }
-
-  public boolean isIfExists() {
-    return ifExists;
-  }
-
-  public void setIfExists(boolean ifExists) {
-    this.ifExists = ifExists;
-  }
-
-  public String toString() {
-    StringBuffer sb = new StringBuffer();
-    sb.append("Index Name : ");
-    sb.append(this.indexName);
-    sb.append("\nIndexed Expression : ");
-    sb.append(this.indexedExpression);
-    sb.append("\nRegion Path : ");
-    sb.append(this.regionPath);
-    sb.append("\nIndex Type : ");
-    sb.append(this.indexType.getName());
-    return sb.toString();
-  }
-
-}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction.java
index 12d5c33..f98e46f 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction.java
@@ -15,12 +15,13 @@
 package org.apache.geode.management.internal.cli.functions;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.cache.execute.ResultSender;
 import org.apache.geode.cache.query.Index;
@@ -28,44 +29,35 @@ import org.apache.geode.cache.query.IndexType;
 import org.apache.geode.cache.query.MultiIndexCreationException;
 import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.internal.cache.execute.InternalFunction;
-import org.apache.geode.internal.cache.xmlcache.CacheXml;
-import org.apache.geode.management.internal.cli.domain.IndexInfo;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
 
-public class CreateDefinedIndexesFunction implements InternalFunction {
-  private static final long serialVersionUID = 1L;
+public class CreateDefinedIndexesFunction implements InternalFunction<Set<RegionConfig.Index>> {
+
+  private static final long serialVersionUID = 6756381106602823693L;
 
   @Override
   public String getId() {
     return CreateDefinedIndexesFunction.class.getName();
   }
 
-  XmlEntity createXmlEntity(final String regionName) {
-    return new XmlEntity(CacheXml.REGION, "name", regionName);
-  }
-
   @Override
-  public void execute(FunctionContext context) {
-    Cache cache;
-    String memberId = null;
-    boolean lastResultSent = Boolean.FALSE;
+  public void execute(FunctionContext<Set<RegionConfig.Index>> context) {
+    Cache cache = context.getCache();
+    QueryService queryService = cache.getQueryService();
+    ResultSender<CliFunctionResult> sender = context.getResultSender();
+    String memberId = cache.getDistributedSystem().getDistributedMember().getId();
+    Set<RegionConfig.Index> indexDefinitions = context.getArguments();
 
     try {
-      cache = context.getCache();
-      ResultSender sender = context.getResultSender();
-      QueryService queryService = cache.getQueryService();
-      memberId = cache.getDistributedSystem().getDistributedMember().getId();
-      Set<IndexInfo> indexDefinitions = (Set<IndexInfo>) context.getArguments();
-
-      for (IndexInfo indexDefinition : indexDefinitions) {
-        String indexName = indexDefinition.getIndexName();
-        String regionPath = indexDefinition.getRegionPath();
-        String indexedExpression = indexDefinition.getIndexedExpression();
+      for (RegionConfig.Index indexDefinition : indexDefinitions) {
+        String indexName = indexDefinition.getName();
+        String regionPath = indexDefinition.getFromClause();
+        String indexedExpression = indexDefinition.getExpression();
+        IndexType indexType = IndexType.valueOfSynonym(indexDefinition.getType());
 
-        if (indexDefinition.getIndexType() == IndexType.PRIMARY_KEY) {
+        if (indexType == IndexType.PRIMARY_KEY) {
           queryService.defineKeyIndex(indexName, indexedExpression, regionPath);
-        } else if (indexDefinition.getIndexType() == IndexType.HASH) {
+        } else if (indexType == IndexType.HASH) {
           queryService.defineHashIndex(indexName, indexedExpression, regionPath);
         } else {
           queryService.defineIndex(indexName, indexedExpression, regionPath);
@@ -73,52 +65,40 @@ public class CreateDefinedIndexesFunction implements InternalFunction {
       }
 
       List<Index> indexes = queryService.createDefinedIndexes();
-      // Build the results with one XmlEntity per region.
-      List<String> processedRegions = new ArrayList<>();
-      List<CliFunctionResult> functionResults = new ArrayList<>();
 
-      for (Index index : indexes) {
-        String regionName = index.getRegion().getName();
-
-        if (!processedRegions.contains(regionName)) {
-          XmlEntity xmlEntity = createXmlEntity(regionName);
-          functionResults.add(new CliFunctionResult(memberId, xmlEntity));
-          processedRegions.add(regionName);
+      if (!indexes.isEmpty()) {
+        for (Index index : indexes) {
+          sender.sendResult(
+              new CliFunctionResult(memberId, true, "Created index " + index.getName()));
         }
+      } else {
+        sender.sendResult(
+            new CliFunctionResult(memberId, true, CliStrings.DEFINE_INDEX__FAILURE__MSG));
       }
 
-      for (Iterator<CliFunctionResult> iterator = functionResults.iterator(); iterator.hasNext();) {
-        CliFunctionResult cliFunctionResult = iterator.next();
+    } catch (MultiIndexCreationException multiIndexCreationException) {
+      // Some indexes may have been created, so let's get those
+      List<String> failedIndexes =
+          new ArrayList<>(multiIndexCreationException.getFailedIndexNames());
+      List<String> createdIndexes =
+          indexDefinitions.stream().filter(i -> !failedIndexes.contains(i.getName()))
+              .map(RegionConfig.Index::getName).collect(Collectors.toList());
 
-        if (iterator.hasNext()) {
-          sender.sendResult(cliFunctionResult);
-        } else {
-          sender.lastResult(cliFunctionResult);
-          lastResultSent = Boolean.TRUE;
-        }
+      for (String index : createdIndexes) {
+        sender.sendResult(new CliFunctionResult(memberId, true, "Created index " + index));
       }
 
-      if (!lastResultSent) {
-        // No indexes were created and no exceptions were thrown during the process.
-        // We still need to make sure the function returns to the caller.
-        sender.lastResult(
-            new CliFunctionResult(memberId, true, CliStrings.DEFINE_INDEX__FAILURE__MSG));
-      }
-    } catch (MultiIndexCreationException multiIndexCreationException) {
-      StringBuffer sb = new StringBuffer();
-      sb.append("Index creation failed for indexes: ").append("\n");
-      for (Map.Entry<String, Exception> failedIndex : multiIndexCreationException.getExceptionsMap()
+      for (Map.Entry<String, Exception> ex : multiIndexCreationException.getExceptionsMap()
           .entrySet()) {
-        sb.append(failedIndex.getKey()).append(" : ").append(failedIndex.getValue().getMessage())
-            .append("\n");
+        sender.sendResult(new CliFunctionResult(memberId, false, String
+            .format("Failed to create index %s: %s", ex.getKey(), ex.getValue().getMessage())));
       }
-      context.getResultSender()
-          .lastResult(new CliFunctionResult(memberId, multiIndexCreationException, sb.toString()));
-    } catch (Exception exception) {
-      String exceptionMessage = CliStrings.format(CliStrings.EXCEPTION_CLASS_AND_MESSAGE,
-          exception.getClass().getName(), exception.getMessage());
-      context.getResultSender()
-          .lastResult(new CliFunctionResult(memberId, exception, exceptionMessage));
+    } catch (Exception ex) {
+      sender.sendResult(new CliFunctionResult(memberId, false, ex.getMessage()));
+    } finally {
+      queryService.clearDefinedIndexes();
     }
+
+    sender.lastResult(null);
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
index 9b3208b..ace6a86 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
@@ -140,7 +140,7 @@ public class CommandExecutor {
         try {
           gfshCommand.updateClusterConfig(group, cc, resultModel.getConfigObject());
           infoResultModel
-              .addLine("Changes to configuration for group '" + group + "' is persisted.");
+              .addLine("Changes to configuration for group '" + group + "' are persisted.");
         } catch (Exception e) {
           String message = "failed to update cluster config for " + group;
           logger.error(message, e);
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
index a7059d9..573d5d9 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
@@ -214,6 +214,9 @@ public class ResultModel {
     boolean atLeastOneSuccess = false;
     section.setColumnHeader("Member", "Status", "Message");
     for (CliFunctionResult functionResult : functionResults) {
+      if (functionResult == null) {
+        continue;
+      }
       section.addRow(functionResult.getMemberIdOrName(), functionResult.getStatus(skipIgnored),
           functionResult.getStatusMessage());
       if (functionResult.isSuccessful()) {
diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
index 1d89eb0..623d59f 100644
--- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
+++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
@@ -511,7 +511,6 @@ org/apache/geode/management/internal/cli/domain/EvictionAttributesInfo,true,1,ev
 org/apache/geode/management/internal/cli/domain/FixedPartitionAttributesInfo,true,1,isPrimary:boolean,numBuckets:int,partitionName:java/lang/String
 org/apache/geode/management/internal/cli/domain/IndexDetails,true,-2198907141534201288,fromClause:java/lang/String,indexName:java/lang/String,indexStatisticsDetails:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,indexType:org/apache/geode/cache/query/IndexType,indexedExpression:java/lang/String,isValid:boolean,memberId:java/lang/String,memberName:java/lang/String,projectionAttributes:java/lang/String,regionName:java/lang/String,regionPath:java/lang/String
 org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,false,numberOfKeys:java/lang/Long,numberOfUpdates:java/lang/Long,numberOfValues:java/lang/Long,totalUpdateTime:java/lang/Long,totalUses:java/lang/Long
-org/apache/geode/management/internal/cli/domain/IndexInfo,true,1,ifExists:boolean,indexName:java/lang/String,indexType:org/apache/geode/cache/query/IndexType,indexedExpression:java/lang/String,regionPath:java/lang/String
 org/apache/geode/management/internal/cli/domain/MemberConfigurationInfo,false,cacheAttributes:java/util/Map,cacheServerAttributes:java/util/List,gfePropsRuntime:java/util/Map,gfePropsSetFromFile:java/util/Map,gfePropsSetUsingApi:java/util/Map,gfePropsSetWithDefaults:java/util/Map,jvmInputArguments:java/util/List,pdxAttributes:java/util/Map,systemProperties:java/util/Properties
 org/apache/geode/management/internal/cli/domain/MemberInformation,true,1,cacheServerList:java/util/List,cacheXmlFilePath:java/lang/String,clientCount:int,cpuUsage:double,groups:java/lang/String,heapUsage:java/lang/String,host:java/lang/String,hostedRegions:java/util/Set,id:java/lang/String,initHeapSize:java/lang/String,isServer:boolean,locatorBindAddress:java/lang/String,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:java/lang/String,name:java/lang/Str [...]
 org/apache/geode/management/internal/cli/domain/MemberResult,true,1,errorMessage:java/lang/String,exceptionMessage:java/lang/String,isSuccessful:boolean,memberNameOrId:java/lang/String,opPossible:boolean,successMessage:java/lang/String
@@ -533,7 +532,7 @@ org/apache/geode/management/internal/cli/functions/CloseDurableCqFunction,true,1
 org/apache/geode/management/internal/cli/functions/ContinuousQueryFunction,true,1
 org/apache/geode/management/internal/cli/functions/ContinuousQueryFunction$ClientInfo,true,1,isDurable:java/lang/String,primaryServer:java/lang/String,secondaryServer:java/lang/String,this$0:org/apache/geode/management/internal/cli/functions/ContinuousQueryFunction
 org/apache/geode/management/internal/cli/functions/CreateAsyncEventQueueFunction,true,1
-org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction,true,1
+org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunction,true,6756381106602823693
 org/apache/geode/management/internal/cli/functions/CreateDiskStoreFunction,true,1
 org/apache/geode/management/internal/cli/functions/CreateIndexFunction,true,1
 org/apache/geode/management/internal/cli/functions/CreateJndiBindingFunction,false
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
index a606a87..77f6fa1 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
@@ -17,7 +17,13 @@ package org.apache.geode.management.internal.cli.commands;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -25,10 +31,14 @@ import org.junit.rules.TestName;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.Region;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.cache.query.Index;
 import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.distributed.Locator;
 import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
 import org.apache.geode.distributed.internal.InternalLocator;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.CommandResult;
 import org.apache.geode.test.dunit.rules.ClusterStartupRule;
 import org.apache.geode.test.dunit.rules.MemberVM;
 import org.apache.geode.test.junit.categories.DistributedTest;
@@ -39,25 +49,29 @@ import org.apache.geode.test.junit.rules.serializable.SerializableTestName;
 
 @Category({DistributedTest.class, OQLIndexTest.class})
 public class CreateDefinedIndexesCommandDUnitTest {
-  private MemberVM locator, server1, server2, server3;
+  private static MemberVM locator, server1, server2, server3;
 
-  @Rule
-  public GfshCommandRule gfsh = new GfshCommandRule();
+  @ClassRule
+  public static GfshCommandRule gfsh = new GfshCommandRule();
 
-  @Rule
-  public ClusterStartupRule clusterStartupRule = new ClusterStartupRule();
+  @ClassRule
+  public static ClusterStartupRule clusterStartupRule = new ClusterStartupRule();
 
   @Rule
   public TestName testName = new SerializableTestName();
 
-  @Before
-  public void before() throws Exception {
+  @BeforeClass
+  public static void beforeClass() throws Exception {
     locator = clusterStartupRule.startLocatorVM(0);
-    server1 = clusterStartupRule.startServerVM(1, locator.getPort());
-    server2 = clusterStartupRule.startServerVM(2, locator.getPort());
-    server3 = clusterStartupRule.startServerVM(3, "group1", locator.getPort());
+    server1 = clusterStartupRule.startServerVM(1, "group1", locator.getPort());
+    server2 = clusterStartupRule.startServerVM(2, "group1", locator.getPort());
+    server3 = clusterStartupRule.startServerVM(3, "group2", locator.getPort());
 
     gfsh.connectAndVerify(locator);
+  }
+
+  @Before
+  public void before() {
     gfsh.executeAndAssertThat("clear defined indexes").statusIsSuccess()
         .containsOutput("Index definitions successfully cleared");
   }
@@ -77,18 +91,24 @@ public class CreateDefinedIndexesCommandDUnitTest {
       assertThat(cache.getRegion(regionName)).isNull();
     }, server1, server2, server3);
 
+    String indexName = "index_" + regionName;
     gfsh.executeAndAssertThat("define index --name=index_" + regionName
         + " --expression=value1 --region=" + regionName + "1").statusIsSuccess()
         .containsOutput("Index successfully defined");
 
-    gfsh.executeAndAssertThat("create defined indexes").statusIsError()
-        .containsOutput("RegionNotFoundException");
+    CommandResult result = gfsh.executeCommand("create defined indexes");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    Map<String, List<String>> table =
+        result.getMapFromTableContent(CreateDefinedIndexesCommand.CREATE_DEFINED_INDEXES_SECTION);
+    assertThat(table.get("Status")).contains("ERROR", "ERROR", "ERROR");
 
     VMProvider.invokeInEveryMember(() -> {
       Cache cache = ClusterStartupRule.getCache();
       QueryService queryService = cache.getQueryService();
 
-      assertThat(queryService.getIndexes().isEmpty()).isTrue();
+      List<String> currentIndexes =
+          queryService.getIndexes().stream().map(Index::getName).collect(Collectors.toList());
+      assertThat(currentIndexes).doesNotContain(indexName);
     }, server1, server2, server3);
   }
 
@@ -123,8 +143,13 @@ public class CreateDefinedIndexesCommandDUnitTest {
         "define index --name=" + index2Name + " --expression=value2 --region=" + region2Name)
         .statusIsSuccess().containsOutput("Index successfully defined");
 
-    gfsh.executeAndAssertThat("create defined indexes").statusIsSuccess()
-        .containsOutput("Indexes successfully created");
+    CommandResult result = gfsh.executeCommand("create defined indexes");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    Map<String, List<String>> table =
+        result.getMapFromTableContent(CreateDefinedIndexesCommand.CREATE_DEFINED_INDEXES_SECTION);
+    assertThat(table.get("Member").size()).isEqualTo(6);
+    assertThat(result.getMessageFromContent())
+        .contains("Changes to configuration for group 'cluster' are persisted");
 
     VMProvider.invokeInEveryMember(() -> {
       Cache cache = ClusterStartupRule.getCache();
@@ -142,9 +167,15 @@ public class CreateDefinedIndexesCommandDUnitTest {
       // Make sure the indexes exist in the cluster config
       InternalConfigurationPersistenceService sharedConfig =
           ((InternalLocator) Locator.getLocator()).getConfigurationPersistenceService();
-      assertThat(sharedConfig.getConfiguration("cluster").getCacheXmlContent()).contains(index1Name,
-          index2Name);
-      assertThat(sharedConfig.getConfiguration("group1")).isNull();
+      RegionConfig region1Config =
+          sharedConfig.getCacheConfig("cluster").findRegionConfiguration(region1Name);
+      assertThat(region1Config.getIndexes().stream().map(RegionConfig.Index::getName)
+          .collect(Collectors.toList())).contains(index1Name);
+
+      RegionConfig region2Config =
+          sharedConfig.getCacheConfig("cluster").findRegionConfiguration(region2Name);
+      assertThat(region2Config.getIndexes().stream().map(RegionConfig.Index::getName)
+          .collect(Collectors.toList())).contains(index2Name);
     });
   }
 
@@ -155,27 +186,29 @@ public class CreateDefinedIndexesCommandDUnitTest {
     String index1Name = "index_" + region1Name;
     String index2Name = "index_" + region2Name;
 
-    gfsh.executeAndAssertThat(
-        "create region --name=" + region1Name + " --type=REPLICATE --group=group1")
-        .statusIsSuccess()
-        .containsOutput("Region \"/" + region1Name + "\" created on \"server-3\"");
+    CommandResult result = gfsh
+        .executeCommand("create region --name=" + region1Name + " --type=REPLICATE --group=group1");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+        "server-2");
 
-    gfsh.executeAndAssertThat(
-        "create region --name=" + region2Name + " --type=REPLICATE --group=group1")
-        .statusIsSuccess()
-        .containsOutput("Region \"/" + region2Name + "\" created on \"server-3\"");
+    result = gfsh
+        .executeCommand("create region --name=" + region2Name + " --type=REPLICATE --group=group1");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+        "server-2");
 
     VMProvider.invokeInEveryMember(() -> {
       Cache cache = ClusterStartupRule.getCache();
       assertThat(cache.getRegion(region1Name)).isNull();
       assertThat(cache.getRegion(region2Name)).isNull();
-    }, server1, server2);
+    }, server3);
 
     VMProvider.invokeInEveryMember(() -> {
       Cache cache = ClusterStartupRule.getCache();
       assertThat(cache.getRegion(region1Name)).isNotNull();
       assertThat(cache.getRegion(region2Name)).isNotNull();
-    }, server3);
+    }, server1, server2);
 
     gfsh.executeAndAssertThat(
         "define index --name=" + index1Name + " --expression=value1 --region=" + region1Name)
@@ -186,7 +219,7 @@ public class CreateDefinedIndexesCommandDUnitTest {
         .statusIsSuccess().containsOutput("Index successfully defined");
 
     gfsh.executeAndAssertThat("create defined indexes --group=group1").statusIsSuccess()
-        .containsOutput("Indexes successfully created");
+        .containsOutput("Changes to configuration for group 'group1' are persisted.");
 
     VMProvider.invokeInEveryMember(() -> {
       Cache cache = ClusterStartupRule.getCache();
@@ -198,7 +231,7 @@ public class CreateDefinedIndexesCommandDUnitTest {
       assertThat(queryService.getIndexes(region2).size()).isEqualTo(1);
       assertThat(queryService.getIndex(region1, index1Name)).isNotNull();
       assertThat(queryService.getIndex(region2, index2Name)).isNotNull();
-    }, server3);
+    }, server1, server2);
 
     locator.invoke(() -> {
       // Make sure the indexes exist in the cluster config
@@ -206,7 +239,67 @@ public class CreateDefinedIndexesCommandDUnitTest {
           ((InternalLocator) Locator.getLocator()).getConfigurationPersistenceService();
       assertThat(sharedConfig.getConfiguration("group1").getCacheXmlContent()).contains(index2Name,
           index1Name);
-      assertThat(sharedConfig.getConfiguration("cluster").getCacheXmlContent()).isNullOrEmpty();
     });
   }
+
+  @Test
+  public void disjointRegionsAndGroupsFailsToDefineIndexes() {
+    String region1Name = testName.getMethodName() + "1";
+    String region2Name = testName.getMethodName() + "2";
+    String index1Name = "index_" + region1Name;
+    String index2Name = "index_" + region2Name;
+
+    CommandResult result = gfsh
+        .executeCommand("create region --name=" + region1Name + " --type=REPLICATE --group=group1");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+        "server-2");
+
+    result = gfsh
+        .executeCommand("create region --name=" + region2Name + " --type=REPLICATE --group=group2");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-3");
+
+    gfsh.executeAndAssertThat(
+        "define index --name=" + index1Name + " --expression=value1 --region=" + region1Name)
+        .statusIsSuccess().containsOutput("Index successfully defined");
+
+    gfsh.executeAndAssertThat(
+        "define index --name=" + index2Name + " --expression=value1 --region=" + region2Name)
+        .statusIsSuccess().containsOutput("Index successfully defined");
+
+    result = gfsh.executeCommand("create defined indexes --group=group1,group2");
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(
+        result.getMapFromTableContent(CreateDefinedIndexesCommand.CREATE_DEFINED_INDEXES_SECTION)
+            .get("Status")).contains("ERROR", "ERROR", "ERROR");
+
+    VMProvider.invokeInEveryMember(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      QueryService queryService = cache.getQueryService();
+      Region region1 = cache.getRegion(region1Name);
+
+      // Returns null instead of an empty collection if there are no indexes...
+      assertThat(queryService.getIndexes(region1)).isNull();
+    }, server1, server2);
+
+    VMProvider.invokeInEveryMember(() -> {
+      Cache cache = ClusterStartupRule.getCache();
+      QueryService queryService = cache.getQueryService();
+      Region region2 = cache.getRegion(region2Name);
+
+      assertThat(queryService.getIndexes(region2)).isNull();
+    }, server3);
+
+    locator.invoke(() -> {
+      // Make sure the indexes do not exist in the cluster config
+      InternalConfigurationPersistenceService sharedConfig =
+          ((InternalLocator) Locator.getLocator()).getConfigurationPersistenceService();
+      assertThat(sharedConfig.getConfiguration("group1").getCacheXmlContent())
+          .doesNotContain(index1Name);
+      assertThat(sharedConfig.getConfiguration("group2").getCacheXmlContent())
+          .doesNotContain(index2Name);
+    });
+  }
+
 }
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandTest.java
index e8ecaeb..90661ed 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandTest.java
@@ -21,14 +21,16 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.junit.Before;
@@ -41,10 +43,8 @@ import org.apache.geode.cache.execute.ResultCollector;
 import org.apache.geode.cache.query.IndexType;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
-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.result.CommandResult;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
 import org.apache.geode.test.junit.categories.UnitTest;
 import org.apache.geode.test.junit.rules.GfshParserRule;
 
@@ -74,8 +74,7 @@ public class CreateDefinedIndexesCommandTest {
 
   @Test
   public void noMembers() throws Exception {
-    IndexDefinition.indexDefinitions
-        .add(new IndexInfo("indexName", "indexedExpression", "TestRegion", IndexType.FUNCTIONAL));
+    IndexDefinition.addIndex("indexName", "indexedExpression", "TestRegion", IndexType.FUNCTIONAL);
     doReturn(Collections.EMPTY_SET).when(command).findMembers(any(), any());
     result = gfshParser.executeCommandWithInstance(command, "create defined indexes");
     assertThat(result.getStatus()).isEqualTo(ERROR);
@@ -94,17 +93,14 @@ public class CreateDefinedIndexesCommandTest {
     doReturn(Arrays.asList(new CliFunctionResult(member.getId(), new Exception("MockException"),
         "Exception Message."))).when(resultCollector).getResult();
 
-    IndexDefinition.indexDefinitions
-        .add(new IndexInfo("index1", "value1", "TestRegion", IndexType.FUNCTIONAL));
+    IndexDefinition.addIndex("index1", "value1", "TestRegion", IndexType.FUNCTIONAL);
     result = gfshParser.executeCommandWithInstance(command, "create defined indexes");
 
     assertThat(result.getStatus()).isEqualTo(ERROR);
-    verify(command, never()).persistClusterConfiguration(any(), any());
   }
 
   @Test
   public void creationSuccess() throws Exception {
-    XmlEntity xmlEntity = mock(XmlEntity.class);
     DistributedMember member = mock(DistributedMember.class);
     when(member.getId()).thenReturn("memberId");
     InternalConfigurationPersistenceService mockService =
@@ -112,23 +108,20 @@ public class CreateDefinedIndexesCommandTest {
 
     doReturn(mockService).when(command).getConfigurationPersistenceService();
     doReturn(Collections.singleton(member)).when(command).findMembers(any(), any());
-    doReturn(Arrays.asList(new CliFunctionResult(member.getId(), xmlEntity))).when(resultCollector)
+    List<String> indexes = new ArrayList<>();
+    indexes.add("index1");
+    doReturn(Arrays.asList(new CliFunctionResult(member.getId(), indexes))).when(resultCollector)
         .getResult();
 
-    IndexDefinition.indexDefinitions
-        .add(new IndexInfo("index1", "value1", "TestRegion", IndexType.FUNCTIONAL));
+    IndexDefinition.addIndex("index1", "value1", "TestRegion", IndexType.FUNCTIONAL);
     result = gfshParser.executeCommandWithInstance(command, "create defined indexes");
 
     assertThat(result.getStatus()).isEqualTo(OK);
-    assertThat(result.failedToPersist()).isFalse();
-    verify(command, Mockito.times(1)).persistClusterConfiguration(any(), any());
-    assertThat(result.getMessageFromContent()).contains("Indexes successfully created");
+    verify(command, Mockito.times(0)).updateClusterConfig(any(), any(), any());
   }
 
   @Test
   public void multipleIndexesOnMultipleRegions() throws Exception {
-    XmlEntity xmlEntityRegion1 = mock(XmlEntity.class);
-    XmlEntity xmlEntityRegion2 = mock(XmlEntity.class);
     DistributedMember member1 = mock(DistributedMember.class);
     DistributedMember member2 = mock(DistributedMember.class);
     when(member1.getId()).thenReturn("memberId_1");
@@ -136,34 +129,25 @@ public class CreateDefinedIndexesCommandTest {
 
     InternalConfigurationPersistenceService mockService =
         mock(InternalConfigurationPersistenceService.class);
-    CliFunctionResult member1Region1Result =
-        new CliFunctionResult(member1.getId(), xmlEntityRegion1);
-    CliFunctionResult member1Region2Result =
-        new CliFunctionResult(member1.getId(), xmlEntityRegion2);
-    CliFunctionResult member2Region1Result =
-        new CliFunctionResult(member2.getId(), xmlEntityRegion2);
-    CliFunctionResult member2Region2Result =
-        new CliFunctionResult(member2.getId(), xmlEntityRegion2);
+    CliFunctionResult resultMember1 =
+        new CliFunctionResult(member1.getId(), Arrays.asList("index1", "index2"));
+    CliFunctionResult resultMember2 =
+        new CliFunctionResult(member2.getId(), Arrays.asList("index1", "index2"));
 
     doReturn(mockService).when(command).getConfigurationPersistenceService();
-    doReturn(new HashSet<>(Arrays.asList(new DistributedMember[] {member1, member2}))).when(command)
-        .findMembers(any(), any());
-    doReturn(Arrays.asList(new CliFunctionResult[] {member1Region1Result, member1Region2Result,
-        member2Region1Result, member2Region2Result})).when(resultCollector).getResult();
+    doReturn(new HashSet<>(Arrays.asList(member1, member2))).when(command).findMembers(any(),
+        any());
+    doReturn(Arrays.asList(resultMember1, resultMember2)).when(resultCollector).getResult();
 
-    IndexDefinition.indexDefinitions
-        .add(new IndexInfo("index1", "value1", "TestRegion1", IndexType.FUNCTIONAL));
-    IndexDefinition.indexDefinitions
-        .add(new IndexInfo("index2", "value2", "TestRegion2", IndexType.FUNCTIONAL));
+    IndexDefinition.addIndex("index1", "value1", "TestRegion1", IndexType.FUNCTIONAL);
+    IndexDefinition.addIndex("index2", "value2", "TestRegion2", IndexType.FUNCTIONAL);
 
     result = gfshParser.executeCommandWithInstance(command, "create defined indexes");
 
     assertThat(result.getStatus()).isEqualTo(OK);
-    assertThat(result.failedToPersist()).isFalse();
 
-    // The command will receive 4 results from 2 members, but we need to persist only 2 (#regions)
-    // of them.
-    verify(command, Mockito.times(2)).persistClusterConfiguration(any(), any());
-    assertThat(result.getMessageFromContent()).contains("Indexes successfully created");
+    Map<String, List<String>> table =
+        result.getMapFromTableContent(CreateDefinedIndexesCommand.CREATE_DEFINED_INDEXES_SECTION);
+    assertThat(table.get("Status")).contains("OK", "OK", "OK", "OK");
   }
 }
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateJndiBindingCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateJndiBindingCommandTest.java
index fd6ca25..d20c470 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateJndiBindingCommandTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateJndiBindingCommandTest.java
@@ -204,7 +204,7 @@ public class CreateJndiBindingCommandTest {
     gfsh.executeAndAssertThat(command,
         COMMAND + " --type=SIMPLE --name=name --jdbc-driver-class=driver --connection-url=url")
         .statusIsSuccess().containsOutput("No members found.")
-        .containsOutput("Changes to configuration for group 'cluster' is persisted.");
+        .containsOutput("Changes to configuration for group 'cluster' are persisted.");
 
     verify(clusterConfigService).updateCacheConfig(any(), any());
     verify(command).updateClusterConfig(eq("cluster"), eq(cacheConfig), any());
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyJndiBindingCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyJndiBindingCommandTest.java
index 4caf03d..093c97e 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyJndiBindingCommandTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DestroyJndiBindingCommandTest.java
@@ -134,7 +134,7 @@ public class DestroyJndiBindingCommandTest {
 
     gfsh.executeAndAssertThat(command, COMMAND + " --name=name").statusIsSuccess()
         .containsOutput("No members found.")
-        .containsOutput("Changes to configuration for group 'cluster' is persisted.");
+        .containsOutput("Changes to configuration for group 'cluster' are persisted.");
 
     verify(ccService).updateCacheConfig(any(), any());
     verify(command).updateClusterConfig(eq("cluster"), eq(cacheConfig), any());
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunctionTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunctionTest.java
index 677ae6b..894fef4 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunctionTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/CreateDefinedIndexesFunctionTest.java
@@ -16,7 +16,6 @@
 package org.apache.geode.management.internal.cli.functions;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -36,6 +35,7 @@ import org.junit.experimental.categories.Category;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.Region;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.cache.execute.ResultSender;
 import org.apache.geode.cache.query.Index;
@@ -48,8 +48,6 @@ import org.apache.geode.cache.query.internal.index.CompactMapRangeIndex;
 import org.apache.geode.cache.query.internal.index.HashIndex;
 import org.apache.geode.cache.query.internal.index.PrimaryKeyIndex;
 import org.apache.geode.internal.cache.execute.FunctionContextImpl;
-import org.apache.geode.management.internal.cli.domain.IndexInfo;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
 import org.apache.geode.test.fake.Fakes;
 import org.apache.geode.test.junit.categories.UnitTest;
 
@@ -61,7 +59,7 @@ public class CreateDefinedIndexesFunctionTest {
   private FunctionContext context;
   private QueryService queryService;
   private TestResultSender resultSender;
-  private Set<IndexInfo> indexDefinitions;
+  private Set<RegionConfig.Index> indexDefinitions;
   private CreateDefinedIndexesFunction function;
 
   @Before
@@ -75,9 +73,30 @@ public class CreateDefinedIndexesFunctionTest {
     doReturn(queryService).when(cache).getQueryService();
 
     indexDefinitions = new HashSet<>();
-    indexDefinitions.add(new IndexInfo("index1", "value1", "/Region1", IndexType.HASH));
-    indexDefinitions.add(new IndexInfo("index2", "value2", "/Region2", IndexType.FUNCTIONAL));
-    indexDefinitions.add(new IndexInfo("index3", "value3", "/Region1", IndexType.PRIMARY_KEY));
+    indexDefinitions.add(new RegionConfig.Index() {
+      {
+        setName("index1");
+        setExpression("value1");
+        setFromClause("/Region1");
+        setType(IndexType.HASH.getName());
+      }
+    });
+    indexDefinitions.add(new RegionConfig.Index() {
+      {
+        setName("index2");
+        setExpression("value2");
+        setFromClause("/Region2");
+        setType(IndexType.FUNCTIONAL.getName());
+      }
+    });
+    indexDefinitions.add(new RegionConfig.Index() {
+      {
+        setName("index3");
+        setExpression("value3");
+        setFromClause("/Region1");
+        setType(IndexType.PRIMARY_KEY.getName());
+      }
+    });
   }
 
   @Test
@@ -89,7 +108,7 @@ public class CreateDefinedIndexesFunctionTest {
     List<?> results = resultSender.getResults();
 
     assertThat(results).isNotNull();
-    assertThat(results.size()).isEqualTo(1);
+    assertThat(results.size()).isEqualTo(2);
     Object firstResult = results.get(0);
     assertThat(firstResult).isInstanceOf(CliFunctionResult.class);
     assertThat(((CliFunctionResult) firstResult).isSuccessful()).isTrue();
@@ -107,7 +126,7 @@ public class CreateDefinedIndexesFunctionTest {
     List<?> results = resultSender.getResults();
 
     assertThat(results).isNotNull();
-    assertThat(results.size()).isEqualTo(1);
+    assertThat(results.size()).isEqualTo(2);
     Object firstResult = results.get(0);
     assertThat(firstResult).isInstanceOf(CliFunctionResult.class);
     assertThat(((CliFunctionResult) firstResult).isSuccessful()).isTrue();
@@ -129,16 +148,21 @@ public class CreateDefinedIndexesFunctionTest {
     List<?> results = resultSender.getResults();
 
     assertThat(results).isNotNull();
-    assertThat(results.size()).isEqualTo(1);
-    Object firstResult = results.get(0);
-    assertThat(firstResult).isNotNull();
-    assertThat(firstResult).isInstanceOf(CliFunctionResult.class);
-    assertThat(((CliFunctionResult) firstResult).isSuccessful()).isFalse();
-    assertThat(((CliFunctionResult) firstResult).getSerializables().length).isEqualTo(1);
-    assertThat(((CliFunctionResult) firstResult).getSerializables()[0]).isNotNull();
-    assertThat(((CliFunctionResult) firstResult).getSerializables()[0].toString()).contains(
-        "Index creation failed for indexes", "index1", "Mock Failure", "index3",
-        "Another Mock Failure");
+    assertThat(results.size()).isEqualTo(4);
+
+    CliFunctionResult result1 = (CliFunctionResult) results.get(0);
+    assertThat(result1.isSuccessful()).isTrue();
+    assertThat(result1.getStatusMessage()).isEqualTo("Created index index2");
+
+    CliFunctionResult result2 = (CliFunctionResult) results.get(1);
+    assertThat(result2.isSuccessful()).isFalse();
+    assertThat(result2.getStatusMessage())
+        .isEqualTo("Failed to create index index1: Mock Failure.");
+
+    CliFunctionResult result3 = (CliFunctionResult) results.get(2);
+    assertThat(result3.isSuccessful()).isFalse();
+    assertThat(result3.getStatusMessage())
+        .isEqualTo("Failed to create index index3: Another Mock Failure.");
   }
 
   @Test
@@ -151,15 +175,11 @@ public class CreateDefinedIndexesFunctionTest {
     List<?> results = resultSender.getResults();
 
     assertThat(results).isNotNull();
-    assertThat(results.size()).isEqualTo(1);
-    Object firstResult = results.get(0);
-    assertThat(firstResult).isNotNull();
-    assertThat(firstResult).isInstanceOf(CliFunctionResult.class);
-    assertThat(((CliFunctionResult) firstResult).isSuccessful()).isFalse();
-    assertThat(((CliFunctionResult) firstResult).getSerializables().length).isEqualTo(1);
-    assertThat(((CliFunctionResult) firstResult).getSerializables()[0]).isNotNull();
-    assertThat(((CliFunctionResult) firstResult).getSerializables()[0].toString())
-        .contains("RuntimeException", "Mock Exception");
+    assertThat(results.size()).isEqualTo(2);
+    CliFunctionResult firstResult = (CliFunctionResult) results.get(0);
+
+    assertThat(firstResult.isSuccessful()).isFalse();
+    assertThat(firstResult.getStatusMessage()).isEqualTo("Mock Exception");
   }
 
   @Test
@@ -176,7 +196,6 @@ public class CreateDefinedIndexesFunctionTest {
     when(index3.getName()).thenReturn("index3");
     when(index3.getRegion()).thenReturn(region1);
 
-    doReturn(mock(XmlEntity.class)).when(function).createXmlEntity(any());
     when(queryService.createDefinedIndexes()).thenReturn(Arrays.asList(index1, index2, index3));
     context = new FunctionContextImpl(cache, CreateDefinedIndexesFunction.class.getName(),
         indexDefinitions, resultSender);
@@ -185,18 +204,12 @@ public class CreateDefinedIndexesFunctionTest {
     List<?> results = resultSender.getResults();
 
     assertThat(results).isNotNull();
-    assertThat(results.size()).isEqualTo(2);
+    assertThat(results.size()).isEqualTo(4);
 
     Object firstIndex = results.get(0);
     assertThat(firstIndex).isNotNull();
     assertThat(firstIndex).isInstanceOf(CliFunctionResult.class);
     assertThat(((CliFunctionResult) firstIndex).isSuccessful());
-
-    Object secondIndex = results.get(0);
-    assertThat(secondIndex).isNotNull();
-    assertThat(secondIndex).isInstanceOf(CliFunctionResult.class);
-    assertThat(((CliFunctionResult) secondIndex).isSuccessful()).isTrue();
-    assertThat(((CliFunctionResult) secondIndex).getXmlEntity()).isNotNull();
   }
 
   private static class TestResultSender implements ResultSender {

-- 
To stop receiving notification emails like this one, please contact
jensdeppe@apache.org.