GEODE-2404: Added gfsh support for destroying a lucene index

Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/834235a5
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/834235a5
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/834235a5

Branch: refs/heads/feature/GEODE-2420
Commit: 834235a56a284aa01aa6af78117d310b530372d8
Parents: af2121d
Author: Barry Oglesby <bogle...@pivotal.io>
Authored: Thu Mar 16 18:25:03 2017 -0700
Committer: Ken Howe <kh...@pivotal.io>
Committed: Mon Mar 27 14:01:44 2017 -0700

----------------------------------------------------------------------
 .../java/org/apache/geode/internal/Version.java |   9 +-
 .../geode/internal/cache/GemFireCacheImpl.java  |   4 +
 .../cache/tier/sockets/CommandInitializer.java  |   7 +-
 .../geode/internal/i18n/LocalizedStrings.java   |   8 +-
 .../configuration/domain/XmlEntity.java         |  85 +++++++++-
 .../internal/configuration/utils/XmlUtils.java  |   8 +-
 .../RollingUpgrade2DUnitTest.java               | 119 +++++++++++++
 .../sanctionedDataSerializables.txt             |   8 +-
 .../internal/LuceneIndexCreationProfile.java    |   7 +-
 .../lucene/internal/LuceneRegionListener.java   | 110 ++++++++++++
 .../lucene/internal/LuceneServiceImpl.java      | 126 +++++++-------
 .../lucene/internal/cli/LuceneCliStrings.java   |  11 +-
 .../internal/cli/LuceneDestroyIndexInfo.java    |  38 +++++
 .../internal/cli/LuceneIndexCommands.java       | 158 ++++++++++++-----
 .../functions/LuceneCreateIndexFunction.java    |   2 +-
 .../functions/LuceneDestroyIndexFunction.java   |  33 +++-
 .../cli/LuceneIndexCommandsDUnitTest.java       | 114 ++++++-------
 .../cli/LuceneIndexCommandsJUnitTest.java       | 168 ++++++++++++++++---
 .../LuceneDestroyIndexFunctionJUnitTest.java    | 122 ++++++++++++--
 .../LuceneClusterConfigurationDUnitTest.java    | 149 ++--------------
 20 files changed, 906 insertions(+), 380 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/internal/Version.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/Version.java 
b/geode-core/src/main/java/org/apache/geode/internal/Version.java
index 0f64e72..288d104 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/Version.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/Version.java
@@ -59,7 +59,7 @@ public final class Version implements Comparable<Version> {
   /** byte used as ordinal to represent this <code>Version</code> */
   private final short ordinal;
 
-  public static final int HIGHEST_VERSION = 50;
+  public static final int HIGHEST_VERSION = 55;
 
   private static final Version[] VALUES = new Version[HIGHEST_VERSION + 1];
 
@@ -180,7 +180,12 @@ public final class Version implements Comparable<Version> {
   public static final Version GFE_90 =
       new Version("GFE", "9.0", (byte) 9, (byte) 0, (byte) 0, (byte) 0, 
GFE_90_ORDINAL);
 
-  private static final byte GFE_91_ORDINAL = 50;
+  private static final byte GEODE_110_ORDINAL = 50;
+
+  public static final Version GEODE_110 =
+      new Version("GEODE", "1.1.0", (byte) 9, (byte) 0, (byte) 1, (byte) 0, 
GEODE_110_ORDINAL);
+
+  private static final byte GFE_91_ORDINAL = 55;
 
   public static final Version GFE_91 =
       new Version("GFE", "9.1", (byte) 9, (byte) 1, (byte) 0, (byte) 0, 
GFE_91_ORDINAL);

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
index fcf7a2a..1575517 100755
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
@@ -3886,6 +3886,10 @@ public class GemFireCacheImpl
     this.regionListeners.remove(l);
   }
 
+  public Set<RegionListener> getRegionListeners() {
+    return Collections.unmodifiableSet(this.regionListeners);
+  }
+
   @SuppressWarnings("unchecked")
   public <T extends CacheService> T getService(Class<T> clazz) {
     return (T) services.get(clazz);

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
index 2f6b963..71586a0 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
@@ -331,8 +331,13 @@ public class CommandInitializer {
       gfe90Commands.put(MessageType.QUERY, QueryGeode10.getCommand());
     }
     {
+      Map<Integer, Command> geode110Commands = new HashMap<Integer, Command>();
+      geode110Commands.putAll(ALL_COMMANDS.get(Version.GFE_90));
+      ALL_COMMANDS.put(Version.GEODE_110, geode110Commands);
+    }
+    {
       Map<Integer, Command> gfe91Commands = new HashMap<Integer, Command>();
-      gfe91Commands.putAll(ALL_COMMANDS.get(Version.GFE_90));
+      gfe91Commands.putAll(ALL_COMMANDS.get(Version.GEODE_110));
       ALL_COMMANDS.put(Version.GFE_91, gfe91Commands);
     }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java 
b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
index 1d288ec..499bf8e 100755
--- 
a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
@@ -7669,14 +7669,16 @@ public class LocalizedStrings {
           "Caught the following exception attempting waitUntilFlushed and will 
return:");
 
   public static final StringId LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1 =
-      new StringId(6651, "Lucene index {0} was not found in region {1}.");
-  public static final StringId LuceneService_DESTROYED_INDEX_0_FROM_REGION_1 =
-      new StringId(6652, "Destroyed Lucene index {0} from region {1}.");
+      new StringId(6651, "Lucene index {0} was not found in region {1}");
+  public static final StringId LuceneService_DESTROYED_INDEX_0_FROM_1_REGION_2 
=
+      new StringId(6652, "Destroyed Lucene index {1} from {0} region {2}");
 
   public static final StringId 
PoolFactoryImpl_CAUGHT_EXCEPTION_ATTEMPTING_TO_ADD_REMOTE_LOCATOR_0 =
       new StringId(6653,
           "Caught the following exception attempting to add remote locator 
{0}. The locator will be ignored.");
 
+  public static final StringId LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0 
=
+      new StringId(6654, "No Lucene indexes were found in region {0}");
   /** Testing strings, messageId 90000-99999 **/
 
   /**

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java
 
b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java
index a009fef..f740dde 100644
--- 
a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java
+++ 
b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java
@@ -31,13 +31,14 @@ import 
javax.xml.transform.TransformerFactoryConfigurationError;
 import javax.xml.xpath.XPathExpressionException;
 
 import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.Version;
+import org.apache.geode.internal.VersionedDataSerializable;
 import org.apache.logging.log4j.Logger;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
-import org.apache.geode.DataSerializable;
 import org.apache.geode.DataSerializer;
 import org.apache.geode.InternalGemFireError;
 import org.apache.geode.cache.Cache;
@@ -54,7 +55,7 @@ import 
org.apache.geode.management.internal.configuration.utils.XmlUtils.XPathCo
  * 
  * 
  */
-public class XmlEntity implements DataSerializable {
+public class XmlEntity implements VersionedDataSerializable {
   private static final long serialVersionUID = 1L;
   private static final Logger logger = LogService.getLogger();
 
@@ -69,6 +70,10 @@ public class XmlEntity implements DataSerializable {
 
   private String namespace = CacheXml.GEODE_NAMESPACE;
 
+  private String childPrefix;
+
+  private String childNamespace;
+
   /**
    * Default constructor for serialization only.
    * 
@@ -113,25 +118,56 @@ public class XmlEntity implements DataSerializable {
     // TODO consider parent as nested XmlEntity type.
     this.parentType = parentType;
     this.type = childType;
+    initializeSearchString(parentKey, parentValue, this.prefix, childKey, 
childValue);
+
+    // no init();
+  }
+
+  /****
+   * Construct a new XmlEntity while creating Xml from the cache using the 
element which has
+   * attributes matching those given
+   *
+   * @param parentType Parent type of the XML element to search for. Should be 
one of the constants
+   *        from the {@link CacheXml} class. For example, CacheXml.REGION.
+   *
+   * @param parentKey Identifier for the parent elements such "name/id"
+   * @param parentValue Value of the identifier
+   * @param childPrefix Namespace prefix for the child element such as "lucene"
+   * @param childNamespace Namespace for the child element such as
+   *        "http://geode.apache.org/schema/lucene";
+   * @param childType Child type of the XML element to search for within the 
parent . Should be one
+   *        of the constants from the {@link CacheXml} class. For example, 
CacheXml.INDEX.
+   * @param childKey Identifier for the child element such as "name/id"
+   * @param childValue Value of the child element identifier
+   */
+  public XmlEntity(final String parentType, final String parentKey, final 
String parentValue,
+      final String childPrefix, final String childNamespace, final String 
childType,
+      final String childKey, final String childValue) {
+    // Note: Do not invoke init
+    this.parentType = parentType;
+    this.type = childType;
+    this.childPrefix = childPrefix;
+    this.childNamespace = childNamespace;
+    initializeSearchString(parentKey, parentValue, childPrefix, childKey, 
childValue);
+  }
 
+  private void initializeSearchString(final String parentKey, final String 
parentValue,
+      final String childPrefix, final String childKey, final String 
childValue) {
     StringBuffer sb = new StringBuffer();
-    sb.append("//").append(prefix).append(':').append(parentType);
+    sb.append("//").append(this.prefix).append(':').append(this.parentType);
 
     if (!StringUtils.isBlank(parentKey) && !StringUtils.isBlank(parentValue)) {
       
sb.append("[@").append(parentKey).append("='").append(parentValue).append("']");
     }
 
-    sb.append("/").append(prefix).append(':').append(childType);
+    sb.append("/").append(childPrefix).append(':').append(this.type);
 
     if (!StringUtils.isBlank(childKey) && !StringUtils.isBlank(childValue)) {
       
sb.append("[@").append(childKey).append("='").append(childValue).append("']");
     }
     this.searchString = sb.toString();
-
-    // no init();
   }
 
-
   /**
    * Initialize new instances. Called from {@link #XmlEntity(String, String, 
String)} and
    * {@link XmlEntityBuilder#build()}.
@@ -312,6 +348,24 @@ public class XmlEntity implements DataSerializable {
     return prefix;
   }
 
+  /**
+   * Gets the prefix for the child element.
+   *
+   * @return XML element prefix for the child element
+   */
+  public String getChildPrefix() {
+    return this.childPrefix;
+  }
+
+  /**
+   * Gets the namespace for the child element.
+   * 
+   * @return XML element namespace for the child element
+   */
+  public String getChildNamespace() {
+    return this.childNamespace;
+  }
+
   @Override
   public String toString() {
     return "XmlEntity [namespace=" + namespace + ", type=" + this.type + ", 
attributes="
@@ -356,6 +410,12 @@ public class XmlEntity implements DataSerializable {
 
   @Override
   public void toData(DataOutput out) throws IOException {
+    toDataPre_GFE_9_1_0_0(out);
+    DataSerializer.writeString(this.childPrefix, out);
+    DataSerializer.writeString(this.childNamespace, out);
+  }
+
+  public void toDataPre_GFE_9_1_0_0(DataOutput out) throws IOException {
     DataSerializer.writeString(this.type, out);
     DataSerializer.writeObject(this.attributes, out);
     DataSerializer.writeString(this.xmlDefinition, out);
@@ -366,6 +426,12 @@ public class XmlEntity implements DataSerializable {
 
   @Override
   public void fromData(DataInput in) throws IOException, 
ClassNotFoundException {
+    fromDataPre_GFE_9_1_0_0(in);
+    this.childPrefix = DataSerializer.readString(in);
+    this.childNamespace = DataSerializer.readString(in);
+  }
+
+  public void fromDataPre_GFE_9_1_0_0(DataInput in) throws IOException, 
ClassNotFoundException {
     this.type = DataSerializer.readString(in);
     this.attributes = DataSerializer.readObject(in);
     this.xmlDefinition = DataSerializer.readString(in);
@@ -384,6 +450,11 @@ public class XmlEntity implements DataSerializable {
     return new XmlEntityBuilder();
   }
 
+  @Override
+  public Version[] getSerializationVersions() {
+    return new Version[] {Version.GFE_91};
+  }
+
   /**
    * Builder for {@link XmlEntity}. Default values are as described in {@link 
XmlEntity}.
    * 

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/XmlUtils.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/XmlUtils.java
 
b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/XmlUtils.java
index 2471c83..a6fafaa 100644
--- 
a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/XmlUtils.java
+++ 
b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/XmlUtils.java
@@ -355,8 +355,12 @@ public class XmlUtils {
    */
   public static NodeList getNodes(Document doc, XmlEntity xmlEntity)
       throws XPathExpressionException {
-    return query(doc, xmlEntity.getSearchString(),
-        new XPathContext(xmlEntity.getPrefix(), xmlEntity.getNamespace()));
+    XPathContext context = new XPathContext();
+    context.addNamespace(xmlEntity.getPrefix(), xmlEntity.getNamespace());
+    if (xmlEntity.getChildPrefix() != null) {
+      context.addNamespace(xmlEntity.getChildPrefix(), 
xmlEntity.getChildNamespace());
+    }
+    return query(doc, xmlEntity.getSearchString(), context);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/test/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgrade2DUnitTest.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgrade2DUnitTest.java
 
b/geode-core/src/test/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgrade2DUnitTest.java
index 54c6d94..beb3a9a 100755
--- 
a/geode-core/src/test/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgrade2DUnitTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgrade2DUnitTest.java
@@ -35,6 +35,12 @@ import org.apache.geode.cache.client.ClientRegionFactory;
 import org.apache.geode.cache.client.ClientRegionShortcut;
 import org.apache.geode.cache.control.RebalanceOperation;
 import org.apache.geode.cache.control.RebalanceResults;
+import org.apache.geode.cache.execute.Execution;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.execute.ResultCollector;
 import org.apache.geode.cache.query.Index;
 import org.apache.geode.cache.query.IndexCreationException;
 import org.apache.geode.cache.query.MultiIndexCreationException;
@@ -43,6 +49,7 @@ import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.cache.query.SelectResults;
 import org.apache.geode.cache.server.CacheServer;
 import org.apache.geode.cache30.CacheSerializableRunnable;
+import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.distributed.DistributedSystem;
 import org.apache.geode.distributed.Locator;
 import org.apache.geode.distributed.internal.DistributionConfig;
@@ -89,9 +96,12 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
+import java.util.Set;
 
 /**
  * This test will not run properly in eclipse at this point due to having to 
bounce vms Currently,
@@ -1006,6 +1016,63 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
 
 
 
+  @Test
+  // This test verifies that an XmlEntity created in the current version 
serializes properly to
+  // previous versions and vice versa.
+  public void testVerifyXmlEntity() throws Exception {
+    doTestVerifyXmlEntity(oldVersion);
+  }
+
+  private void doTestVerifyXmlEntity(String oldVersion) throws Exception {
+    final Host host = Host.getHost(0);
+    VM oldLocator = host.getVM(oldVersion, 0);
+    VM oldServer = host.getVM(oldVersion, 1);
+    VM currentServer1 = host.getVM(2);
+    VM currentServer2 = host.getVM(3);
+
+    int[] locatorPorts = AvailablePortHelper.getRandomAvailableTCPPorts(1);
+    String hostName = NetworkUtils.getServerHostName(host);
+    String locatorsString = getLocatorString(locatorPorts);
+    DistributedTestUtils.deleteLocatorStateFile(locatorPorts);
+
+    try {
+      // Start locator
+      oldLocator.invoke(invokeStartLocator(hostName, locatorPorts[0], 
getTestMethodName(),
+          getLocatorPropertiesPre91(locatorsString)));
+
+      // Start servers
+      
invokeRunnableInVMs(invokeCreateCache(getSystemProperties(locatorPorts)), 
oldServer,
+          currentServer1, currentServer2);
+      currentServer1.invoke(invokeAssertVersion(Version.CURRENT_ORDINAL));
+      currentServer2.invoke(invokeAssertVersion(Version.CURRENT_ORDINAL));
+
+      // Get DistributedMembers of the servers
+      DistributedMember oldServerMember = oldServer.invoke(() -> 
getDistributedMember());
+      DistributedMember currentServer1Member = currentServer1.invoke(() -> 
getDistributedMember());
+      DistributedMember currentServer2Member = currentServer2.invoke(() -> 
getDistributedMember());
+
+      // Register function in all servers
+      Function function = new GetDataSerializableFunction();
+      invokeRunnableInVMs(invokeRegisterFunction(function), oldServer, 
currentServer1,
+          currentServer2);
+
+      // Execute the function in the old server against the other servers to 
verify the
+      // DataSerializable can be serialized from a newer server to an older 
one.
+      oldServer.invoke(() -> executeFunctionAndVerify(function.getId(),
+          
"org.apache.geode.management.internal.configuration.domain.XmlEntity",
+          currentServer1Member, currentServer2Member));
+
+      // Execute the function in a new server against the other servers to 
verify the
+      // DataSerializable can be serialized from an older server to a newer 
one.
+      currentServer1.invoke(() -> executeFunctionAndVerify(function.getId(),
+          
"org.apache.geode.management.internal.configuration.domain.XmlEntity", 
oldServerMember,
+          currentServer2Member));
+    } finally {
+      invokeRunnableInVMs(true, invokeStopLocator(), oldLocator);
+      invokeRunnableInVMs(true, invokeCloseCache(), oldServer, currentServer1, 
currentServer2);
+    }
+  }
+
   // ******** TEST HELPER METHODS ********/
   private void putAndVerify(String objectType, VM putter, String regionName, 
int start, int end,
       VM check1, VM check2, VM check3) throws Exception {
@@ -1090,6 +1157,19 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
 
   }
 
+  private void executeFunctionAndVerify(String functionId, String dsClassName,
+      DistributedMember... members) {
+    Set<DistributedMember> membersSet = new HashSet<>();
+    Collections.addAll(membersSet, members);
+    Execution execution = 
FunctionService.onMembers(membersSet).withArgs(dsClassName);
+    ResultCollector rc = execution.execute(functionId);
+    List result = (List) rc.getResult();
+    assertEquals(membersSet.size(), result.size());
+    for (Iterator i = result.iterator(); i.hasNext();) {
+      assertTrue(i.next().getClass().getName().equals(dsClassName));
+    }
+  }
+
   private void query(String queryString, int numExpectedResults, VM... vms) {
     for (VM vm : vms) {
       vm.invoke(invokeAssertQueryResults(queryString, numExpectedResults));
@@ -1525,6 +1605,18 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
     };
   }
 
+  private CacheSerializableRunnable invokeRegisterFunction(final Function 
function) {
+    return new CacheSerializableRunnable("invokeRegisterFunction") {
+      public void run2() {
+        try {
+          registerFunction(function, RollingUpgrade2DUnitTest.cache);
+        } catch (Exception e) {
+          fail("Error registering function ", e);
+        }
+      }
+    };
+  }
+
   public void deleteDiskStores() throws Exception {
     try {
       FileUtils.deleteDirectory(new File(diskDir).getAbsoluteFile());
@@ -1568,6 +1660,10 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
     return cache.getRegion(regionName);
   }
 
+  public static DistributedMember getDistributedMember() {
+    return cache.getDistributedSystem().getDistributedMember();
+  }
+
   public static boolean assertEntriesCorrect(GemFireCache cache, String 
regionName, int start,
       int end) throws Exception {
     assertRegionExists(cache, regionName);
@@ -1731,6 +1827,10 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
     }
   }
 
+  public static void registerFunction(Function function, GemFireCache cache) {
+    FunctionService.registerFunction(function);
+  }
+
   public static void stopCacheServers(GemFireCache cache) throws Exception {
     List<CacheServer> servers = ((Cache) cache).getCacheServers();
     for (CacheServer server : servers) {
@@ -1813,5 +1913,24 @@ public class RollingUpgrade2DUnitTest extends 
JUnit4DistributedTestCase {
     return Host.getHost(0).getHostName();
   }
 
+  public static class GetDataSerializableFunction implements Function {
+
+    @Override
+    public void execute(FunctionContext context) {
+      String dsClassName = (String) context.getArguments();
+      try {
+        Class aClass = 
Thread.currentThread().getContextClassLoader().loadClass(dsClassName);
+        Constructor constructor = aClass.getConstructor(new Class[0]);
+        context.getResultSender().lastResult(constructor.newInstance(new 
Object[0]));
+      } catch (Exception e) {
+        throw new FunctionException(e);
+      }
+    }
+
+    @Override
+    public String getId() {
+      return GetDataSerializableFunction.class.getName();
+    }
+  }
 }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
 
b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
index 0a791c4..2f5a5cd 100644
--- 
a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
+++ 
b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
@@ -2078,9 +2078,11 @@ 
org/apache/geode/management/internal/configuration/domain/ConfigurationChangeRes
 fromData,31,2a2bb80019b6001ab500022a2bb8001bb500032a2bb8001cc0001db50004b1
 toData,28,2ab40002b800152bb800162ab400032bb800172ab400042bb80018b1
 
-org/apache/geode/management/internal/configuration/domain/XmlEntity,2
-fromData,52,2a2bb80065b500072a2bb80066c00067b500042a2bb80065b500032a2bb80065b5001c2a2bb80065b500062a2bb80065b50005b1
-toData,49,2ab400072bb800632ab400042bb800642ab400032bb800632ab4001c2bb800632ab400062bb800632ab400052bb80063b1
+org/apache/geode/management/internal/configuration/domain/XmlEntity,4
+fromData,22,2a2bb600692a2bb8006ab500122a2bb8006ab50013b1
+fromDataPre_GFE_9_1_0_0,52,2a2bb8006ab500072a2bb8006bc0006cb500042a2bb8006ab500032a2bb8006ab5001f2a2bb8006ab500062a2bb8006ab50005b1
+toData,22,2a2bb600662ab400122bb800672ab400132bb80067b1
+toDataPre_GFE_9_1_0_0,49,2ab400072bb800672ab400042bb800682ab400032bb800672ab4001f2bb800672ab400062bb800672ab400052bb80067b1
 
 
org/apache/geode/management/internal/configuration/messages/ConfigurationRequest,2
 
fromData,73,2a2bb900130100b500052bb9001401003dbb000259b700034e1c9e001f03360415041ca200162d2bb900150100b90009020057840401a7ffea2a2db500042a2bb900140100b50007b1

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java
index 1c20209..0e28bab 100644
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java
@@ -158,9 +158,10 @@ public class LuceneIndexCreationProfile implements 
CacheServiceProfile, DataSeri
 
   public String toString() {
     return new 
StringBuilder().append(getClass().getSimpleName()).append("[").append("indexName=")
-        .append(this.indexName).append("; 
fieldNames=").append(Arrays.toString(this.fieldNames))
-        .append("; analyzerClass=").append(this.analyzerClass).append("; 
fieldAnalyzers=")
-        .append(this.fieldAnalyzers).append("]").toString();
+        .append(this.indexName).append("; regionPath=").append(this.regionPath)
+        .append("; 
fieldNames=").append(Arrays.toString(this.fieldNames)).append("; 
analyzerClass=")
+        .append(this.analyzerClass).append("; 
fieldAnalyzers=").append(this.fieldAnalyzers)
+        .append("]").toString();
   }
 
   public String getRegionPath() {

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java
new file mode 100644
index 0000000..c2109e4
--- /dev/null
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java
@@ -0,0 +1,110 @@
+/*
+ * 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.cache.lucene.internal;
+
+import org.apache.geode.cache.AttributesFactory;
+import org.apache.geode.cache.EvictionAlgorithm;
+import org.apache.geode.cache.EvictionAttributes;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionAttributes;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.cache.InternalRegionArguments;
+import org.apache.geode.internal.cache.RegionListener;
+import org.apache.lucene.analysis.Analyzer;
+
+import java.util.Map;
+
+public class LuceneRegionListener implements RegionListener {
+
+  private final LuceneServiceImpl service;
+
+  private final GemFireCacheImpl cache;
+
+  private final String indexName;
+
+  private final String regionPath;
+
+  private final Analyzer analyzer;
+
+  private final Map<String, Analyzer> fieldAnalyzers;
+
+  private final String[] fields;
+
+  public LuceneRegionListener(LuceneServiceImpl service, GemFireCacheImpl 
cache, String indexName,
+      String regionPath, String[] fields, Analyzer analyzer, Map<String, 
Analyzer> fieldAnalyzers) {
+    this.service = service;
+    this.cache = cache;
+    this.indexName = indexName;
+    this.regionPath = regionPath;
+    this.fields = fields;
+    this.analyzer = analyzer;
+    this.fieldAnalyzers = fieldAnalyzers;
+  }
+
+  public String getRegionPath() {
+    return this.regionPath;
+  }
+
+  public String getIndexName() {
+    return this.indexName;
+  }
+
+  @Override
+  public RegionAttributes beforeCreate(Region parent, String regionName, 
RegionAttributes attrs,
+      InternalRegionArguments internalRegionArgs) {
+    RegionAttributes updatedRA = attrs;
+    String path = parent == null ? "/" + regionName : parent.getFullPath() + 
"/" + regionName;
+
+    if (path.equals(this.regionPath)) {
+
+      if (!attrs.getDataPolicy().withPartitioning()) {
+        // replicated region
+        throw new UnsupportedOperationException(
+            "Lucene indexes on replicated regions are not supported");
+      }
+
+      // For now we cannot support eviction with local destroy.
+      // Eviction with overflow to disk still needs to be supported
+      EvictionAttributes evictionAttributes = attrs.getEvictionAttributes();
+      EvictionAlgorithm evictionAlgorithm = evictionAttributes.getAlgorithm();
+      if (evictionAlgorithm != EvictionAlgorithm.NONE
+          && evictionAttributes.getAction().isLocalDestroy()) {
+        throw new UnsupportedOperationException(
+            "Lucene indexes on regions with eviction and action local destroy 
are not supported");
+      }
+
+      String aeqId = LuceneServiceImpl.getUniqueIndexName(this.indexName, 
this.regionPath);
+      if (!attrs.getAsyncEventQueueIds().contains(aeqId)) {
+        AttributesFactory af = new AttributesFactory(attrs);
+        af.addAsyncEventQueueId(aeqId);
+        updatedRA = af.create();
+      }
+
+      // Add index creation profile
+      internalRegionArgs.addCacheServiceProfile(new 
LuceneIndexCreationProfile(this.indexName,
+          this.regionPath, this.fields, this.analyzer, this.fieldAnalyzers));
+    }
+    return updatedRA;
+  }
+
+  @Override
+  public void afterCreate(Region region) {
+    if (region.getFullPath().equals(this.regionPath)) {
+      this.service.afterDataRegionCreated(this.indexName, this.analyzer, 
this.regionPath,
+          this.fieldAnalyzers, this.fields);
+      this.cache.removeRegionListener(this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java
index 41d2991..dbe24ff 100644
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java
@@ -160,57 +160,10 @@ public class LuceneServiceImpl implements 
InternalLuceneService {
       throw new IllegalStateException("The lucene index must be created before 
region");
     }
 
-    final String dataRegionPath = regionPath;
-    cache.addRegionListener(new RegionListener() {
-      @Override
-      public RegionAttributes beforeCreate(Region parent, String regionName, 
RegionAttributes attrs,
-          InternalRegionArguments internalRegionArgs) {
-        RegionAttributes updatedRA = attrs;
-        String path = parent == null ? "/" + regionName : parent.getFullPath() 
+ "/" + regionName;
-
-        if (path.equals(dataRegionPath)) {
-
-          if (!attrs.getDataPolicy().withPartitioning()) {
-            // replicated region
-            throw new UnsupportedOperationException(
-                "Lucene indexes on replicated regions are not supported");
-          }
-
-          // For now we cannot support eviction with local destroy.
-          // Eviction with overflow to disk still needs to be supported
-          EvictionAttributes evictionAttributes = 
attrs.getEvictionAttributes();
-          EvictionAlgorithm evictionAlgorithm = 
evictionAttributes.getAlgorithm();
-          if (evictionAlgorithm != EvictionAlgorithm.NONE
-              && evictionAttributes.getAction().isLocalDestroy()) {
-            throw new UnsupportedOperationException(
-                "Lucene indexes on regions with eviction and action local 
destroy are not supported");
-          }
-
-          String aeqId = LuceneServiceImpl.getUniqueIndexName(indexName, 
dataRegionPath);
-          if (!attrs.getAsyncEventQueueIds().contains(aeqId)) {
-            AttributesFactory af = new AttributesFactory(attrs);
-            af.addAsyncEventQueueId(aeqId);
-            updatedRA = af.create();
-          }
-
-          // Add index creation profile
-          internalRegionArgs.addCacheServiceProfile(new 
LuceneIndexCreationProfile(indexName,
-              dataRegionPath, fields, analyzer, fieldAnalyzers));
-        }
-        return updatedRA;
-      }
-
-      @Override
-      public void afterCreate(Region region) {
-        if (region.getFullPath().equals(dataRegionPath)) {
-          afterDataRegionCreated(indexName, analyzer, dataRegionPath, 
fieldAnalyzers, fields);
-          cache.removeRegionListener(this);
-        }
-      }
-    });
+    cache.addRegionListener(new LuceneRegionListener(this, cache, indexName, 
regionPath, fields,
+        analyzer, fieldAnalyzers));
   }
 
-
   /**
    * Finish creating the lucene index after the data region is created .
    * 
@@ -273,14 +226,38 @@ public class LuceneServiceImpl implements 
InternalLuceneService {
     }
     LuceneIndexImpl indexImpl = (LuceneIndexImpl) getIndex(indexName, 
regionPath);
     if (indexImpl == null) {
-      throw new IllegalArgumentException(
-          
LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1.toLocalizedString(indexName,
-              regionPath));
+      destroyDefinedIndex(indexName, regionPath);
     } else {
       indexImpl.destroy(initiator);
       removeFromIndexMap(indexImpl);
-      
logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_REGION_1
-          .toLocalizedString(indexName, regionPath));
+      
logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_1_REGION_2
+          .toLocalizedString(indexName, "initialized", regionPath));
+    }
+  }
+
+  public void destroyDefinedIndex(String indexName, String regionPath) {
+    String uniqueIndexName = LuceneServiceImpl.getUniqueIndexName(indexName, 
regionPath);
+    if (definedIndexMap.containsKey(uniqueIndexName)) {
+      definedIndexMap.remove(uniqueIndexName);
+      RegionListener listenerToRemove = null;
+      for (RegionListener listener : cache.getRegionListeners()) {
+        if (listener instanceof LuceneRegionListener) {
+          LuceneRegionListener lrl = (LuceneRegionListener) listener;
+          if (lrl.getRegionPath().equals(regionPath) && 
lrl.getIndexName().equals(indexName)) {
+            listenerToRemove = lrl;
+            break;
+          }
+        }
+      }
+      if (listenerToRemove != null) {
+        cache.removeRegionListener(listenerToRemove);
+      }
+      
logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_1_REGION_2
+          .toLocalizedString(indexName, "defined", regionPath));
+    } else {
+      throw new IllegalArgumentException(
+          
LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1.toLocalizedString(indexName,
+              regionPath));
     }
   }
 
@@ -301,10 +278,43 @@ public class LuceneServiceImpl implements 
InternalLuceneService {
         indexesToDestroy.add(indexImpl);
       }
     }
-    for (LuceneIndex index : indexesToDestroy) {
-      removeFromIndexMap(index);
-      
logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_REGION_1
-          .toLocalizedString(index.getName(), regionPath));
+
+    // If list is empty throw an exception; otherwise iterate and destroy the 
defined index
+    if (indexesToDestroy.isEmpty()) {
+      throw new IllegalArgumentException(
+          LocalizedStrings.LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0
+              .toLocalizedString(regionPath));
+    } else {
+      for (LuceneIndex index : indexesToDestroy) {
+        removeFromIndexMap(index);
+        
logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_1_REGION_2
+            .toLocalizedString(index.getName(), "initialized", regionPath));
+      }
+    }
+  }
+
+  public void destroyDefinedIndexes(String regionPath) {
+    if (!regionPath.startsWith("/")) {
+      regionPath = "/" + regionPath;
+    }
+
+    // Iterate the defined indexes to get the ones for the regionPath
+    List<LuceneIndexCreationProfile> indexesToDestroy = new ArrayList<>();
+    for (Map.Entry<String, LuceneIndexCreationProfile> entry : 
definedIndexMap.entrySet()) {
+      if (entry.getValue().getRegionPath().equals(regionPath)) {
+        indexesToDestroy.add(entry.getValue());
+      }
+    }
+
+    // If list is empty throw an exception; otherwise iterate and destroy the 
defined index
+    if (indexesToDestroy.isEmpty()) {
+      throw new IllegalArgumentException(
+          LocalizedStrings.LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0
+              .toLocalizedString(regionPath));
+    } else {
+      for (LuceneIndexCreationProfile profile : indexesToDestroy) {
+        destroyDefinedIndex(profile.getIndexName(), profile.getRegionPath());
+      }
     }
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java
index fbb70d2..d0a2999 100644
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java
@@ -44,9 +44,6 @@ public class LuceneCliStrings {
   public static final String LUCENE_CREATE_INDEX__ANALYZER = "analyzer";
   public static final String LUCENE_CREATE_INDEX__ANALYZER_HELP =
       "Type of the analyzer for each field.";
-  public static final String LUCENE_CREATE_INDEX__GROUP = "group";
-  public static final String LUCENE_CREATE_INDEX__GROUP__HELP =
-      "Group of members in which the lucene index will be created.";
   public static final String CREATE_INDEX__SUCCESS__MSG =
       "Index successfully created with following details";
   public static final String CREATE_INDEX__FAILURE__MSG =
@@ -109,8 +106,8 @@ public class LuceneCliStrings {
       "Index cannot be empty.";
   public static final String 
LUCENE_DESTROY_INDEX__MSG__COULDNOT_FIND_MEMBERS_FOR_REGION_0 =
       "Could not find any members defining region {0}.";
-  public static final String 
LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FOR_REGION_0 =
-      "Successfully destroyed all lucene indexes for region {0}";
-  public static final String 
LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FOR_REGION_1 =
-      "Successfully destroyed lucene index {0} for region {1}";
+  public static final String 
LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0 =
+      "Successfully destroyed all lucene indexes from region {0}";
+  public static final String 
LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1 =
+      "Successfully destroyed lucene index {0} from region {1}";
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneDestroyIndexInfo.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneDestroyIndexInfo.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneDestroyIndexInfo.java
new file mode 100644
index 0000000..d839ce9
--- /dev/null
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneDestroyIndexInfo.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cache.lucene.internal.cli;
+
+public class LuceneDestroyIndexInfo extends LuceneFunctionSerializable {
+
+  private boolean definedDestroyOnly;
+
+  public LuceneDestroyIndexInfo(final String indexName, final String 
regionPath) {
+    this(indexName, regionPath, false);
+  }
+
+  public LuceneDestroyIndexInfo(final String indexName, final String 
regionPath,
+      boolean definedDestroyOnly) {
+    super(indexName, regionPath);
+    this.definedDestroyOnly = definedDestroyOnly;
+  }
+
+  public void setDefinedDestroyOnly(boolean definedDestroyOnly) {
+    this.definedDestroyOnly = definedDestroyOnly;
+  }
+
+  public boolean isDefinedDestroyOnly() {
+    return this.definedDestroyOnly;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
index e2d85a6..3fa34e7 100755
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
@@ -22,6 +22,7 @@ import org.apache.geode.cache.lucene.internal.cli.functions.*;
 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.logging.LogService;
 import org.apache.geode.internal.security.IntegratedSecurityService;
 import org.apache.geode.internal.security.SecurityService;
 import org.apache.geode.management.cli.CliMetaData;
@@ -44,6 +45,8 @@ 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.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -173,13 +176,7 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
       @CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER, 
mandatory = false,
           unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
           help = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER_HELP) 
@CliMetaData(
-              valueSeparator = ",") final String[] analyzers,
-
-      @CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP,
-          optionContext = ConverterHint.MEMBERGROUP,
-          unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
-          help = LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP__HELP) 
@CliMetaData(
-              valueSeparator = ",") final String[] groups) {
+              valueSeparator = ",") final String[] analyzers) {
 
     Result result = null;
     XmlEntity xmlEntity = null;
@@ -189,7 +186,7 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
       final Cache cache = getCache();
       LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath, 
fields, analyzers);
       final ResultCollector<?, ?> rc =
-          this.executeFunctionOnGroups(createIndexFunction, groups, indexInfo);
+          this.executeFunctionOnAllMembers(createIndexFunction, indexInfo);
       final List<CliFunctionResult> funcResults = (List<CliFunctionResult>) 
rc.getResult();
 
       final TabularResultData tabularResult = 
ResultBuilder.createTabularResultData();
@@ -255,7 +252,7 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
   protected List<LuceneIndexDetails> getIndexDetails(LuceneIndexInfo 
indexInfo) throws Exception {
     this.securityService.authorizeRegionManage(indexInfo.getRegionPath());
     final ResultCollector<?, ?> rc =
-        this.executeFunctionOnGroups(describeIndexFunction, new String[] {}, 
indexInfo);
+        executeFunctionOnRegion(describeIndexFunction, indexInfo, true);
     final List<LuceneIndexDetails> funcResults = (List<LuceneIndexDetails>) 
rc.getResult();
     return funcResults.stream().filter(indexDetails -> indexDetails != null)
         .collect(Collectors.toList());
@@ -318,7 +315,6 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
       help = LuceneCliStrings.LUCENE_DESTROY_INDEX__HELP)
   @CliMetaData(shellOnly = false,
       relatedTopic = {CliStrings.TOPIC_GEODE_REGION, 
CliStrings.TOPIC_GEODE_DATA})
-  @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
   public Result destroyIndex(
       @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME, mandatory = false,
           help = LuceneCliStrings.LUCENE_DESTROY_INDEX__NAME__HELP) final 
String indexName,
@@ -338,32 +334,16 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
 
     this.securityService.authorizeRegionManage(regionPath);
 
-    Result result = null;
+    Result result;
     try {
-      LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath);
-      ResultCollector<?, ?> rc = executeFunction(destroyIndexFunction, 
indexInfo, false);
-      List<CliFunctionResult> functionResults = (List<CliFunctionResult>) 
rc.getResult();
-      CliFunctionResult cliFunctionResult = functionResults.get(0);
-
-      final TabularResultData tabularResult = 
ResultBuilder.createTabularResultData();
-      tabularResult.accumulate("Member", 
cliFunctionResult.getMemberIdOrName());
-      if (cliFunctionResult.isSuccessful()) {
-        tabularResult.accumulate("Status",
-            indexName == null
-                ? CliStrings.format(
-                    
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FOR_REGION_0,
-                    new Object[] {regionPath})
-                : CliStrings.format(
-                    
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FOR_REGION_1,
-                    new Object[] {indexName, regionPath}));
-      } else {
-        tabularResult.accumulate("Status", "Failed: " + 
cliFunctionResult.getMessage());
-      }
-      result = ResultBuilder.buildResult(tabularResult);
-      if (cliFunctionResult.isSuccessful()) {
+      List<CliFunctionResult> accumulatedResults = new ArrayList<>();
+      final XmlEntity xmlEntity =
+          executeDestroyIndexFunction(accumulatedResults, indexName, 
regionPath);
+      result = getDestroyIndexResult(accumulatedResults, indexName, 
regionPath);
+      if (xmlEntity != null) {
         persistClusterConfiguration(result, () -> {
-          // Update the xml entity (region entity) to remove the async event 
id(s) and index(es)
-          getSharedConfiguration().addXmlEntity((XmlEntity) 
cliFunctionResult.getXmlEntity(), null);
+          // Delete the xml entity to remove the index(es) in all groups
+          getSharedConfiguration().deleteXmlEntity(xmlEntity, null);
         });
       }
     } catch (FunctionInvocationTargetException ignore) {
@@ -375,6 +355,7 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
     } catch (IllegalArgumentException e) {
       result = ResultBuilder.createInfoResult(e.getMessage());
     } catch (Throwable t) {
+      t.printStackTrace();
       SystemFailure.checkFailure();
       
getCache().getLogger().warning(LuceneCliStrings.LUCENE_DESTROY_INDEX__EXCEPTION_MESSAGE,
 t);
       result = ResultBuilder.createGemFireErrorResult(t.getMessage());
@@ -382,6 +363,91 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
     return result;
   }
 
+  private XmlEntity executeDestroyIndexFunction(List<CliFunctionResult> 
accumulatedResults,
+      String indexName, String regionPath) {
+    // Destroy has three cases:
+    //
+    // - no members define the region
+    // In this case, send the request to all members to handle the case where 
the index has been
+    // created, but not the region
+    //
+    // - all members define the region
+    // In this case, send the request to one of the region members to destroy 
the index on all
+    // member
+    //
+    // - some members define the region; some don't
+    // In this case, send the request to one of the region members to destroy 
the index in all the
+    // region members. Then send the function to the remaining members to 
handle the case where
+    // the index has been created, but not the region
+    XmlEntity xmlEntity = null;
+    Cache cache = getCache();
+    Set<DistributedMember> regionMembers = getRegionMembers(cache, regionPath);
+    Set<DistributedMember> normalMembers = getNormalMembers(cache);
+    LuceneDestroyIndexInfo indexInfo = new LuceneDestroyIndexInfo(indexName, 
regionPath);
+    ResultCollector<?, ?> rc;
+    if (regionMembers.isEmpty()) {
+      // Attempt to destroy the proxy index on all members
+      indexInfo.setDefinedDestroyOnly(true);
+      rc = executeFunction(destroyIndexFunction, indexInfo, normalMembers);
+      accumulatedResults.addAll((List<CliFunctionResult>) rc.getResult());
+    } else {
+      // Attempt to destroy the index on a region member
+      indexInfo.setDefinedDestroyOnly(false);
+      Set<DistributedMember> singleMember = new HashSet<>();
+      singleMember.add(regionMembers.iterator().next());
+      rc = executeFunction(destroyIndexFunction, indexInfo, singleMember);
+      List<CliFunctionResult> cliFunctionResults = (List<CliFunctionResult>) 
rc.getResult();
+      CliFunctionResult cliFunctionResult = cliFunctionResults.get(0);
+      xmlEntity = cliFunctionResult.getXmlEntity();
+      for (DistributedMember regionMember : regionMembers) {
+        accumulatedResults.add(new CliFunctionResult(regionMember.getId(),
+            cliFunctionResult.isSuccessful(), cliFunctionResult.getMessage()));
+      }
+      // If that succeeds, destroy the proxy index(es) on all other members if 
necessary
+      if (cliFunctionResult.isSuccessful()) {
+        normalMembers.removeAll(regionMembers);
+        if (!normalMembers.isEmpty()) {
+          indexInfo.setDefinedDestroyOnly(true);
+          rc = executeFunction(destroyIndexFunction, indexInfo, normalMembers);
+          accumulatedResults.addAll((List<CliFunctionResult>) rc.getResult());
+        }
+      } else {
+        // @todo Should dummy results be added to the accumulatedResults for 
the non-region
+        // members in the failed case
+      }
+    }
+    return xmlEntity;
+  }
+
+  protected Set<DistributedMember> getRegionMembers(Cache cache, String 
regionPath) {
+    return CliUtil.getMembersForeRegionViaFunction(cache, regionPath, true);
+  }
+
+  protected Set<DistributedMember> getNormalMembers(Cache cache) {
+    return CliUtil.getAllNormalMembers(cache);
+  }
+
+  private Result getDestroyIndexResult(List<CliFunctionResult> 
cliFunctionResults, String indexName,
+      String regionPath) {
+    final TabularResultData tabularResult = 
ResultBuilder.createTabularResultData();
+    for (CliFunctionResult cliFunctionResult : cliFunctionResults) {
+      tabularResult.accumulate("Member", 
cliFunctionResult.getMemberIdOrName());
+      if (cliFunctionResult.isSuccessful()) {
+        tabularResult.accumulate("Status",
+            indexName == null
+                ? CliStrings.format(
+                    
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0,
+                    new Object[] {regionPath})
+                : CliStrings.format(
+                    
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1,
+                    new Object[] {indexName, regionPath}));
+      } else {
+        tabularResult.accumulate("Status", cliFunctionResult.getMessage());
+      }
+    }
+    return ResultBuilder.buildResult(tabularResult);
+  }
+
   private Result displayResults(int pageSize, boolean keysOnly) throws 
Exception {
     if (searchResults.size() == 0) {
       return ResultBuilder
@@ -492,23 +558,18 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
     return ResultBuilder.buildResult(data);
   }
 
-  protected ResultCollector<?, ?> executeFunctionOnGroups(FunctionAdapter 
function, String[] groups,
-      final LuceneIndexInfo indexInfo) throws IllegalArgumentException, 
CommandResultException {
-    ResultCollector<?, ?> results = null;
-    if (function != createIndexFunction) {
-      results = executeFunction(function, indexInfo, true);
-    } else {
-      Set<DistributedMember> targetMembers = 
CliUtil.findMembersOrThrow(groups, null);
-      results = CliUtil.executeFunction(function, indexInfo, targetMembers);
-    }
-    return results;
+  protected ResultCollector<?, ?> executeFunctionOnAllMembers(Function 
function,
+      final LuceneFunctionSerializable functionArguments)
+      throws IllegalArgumentException, CommandResultException {
+    Set<DistributedMember> targetMembers = 
CliUtil.getAllNormalMembers(getCache());
+    return executeFunction(function, functionArguments, targetMembers);
   }
 
   protected ResultCollector<?, ?> executeSearch(final LuceneQueryInfo 
queryInfo) throws Exception {
-    return executeFunction(searchIndexFunction, queryInfo, false);
+    return executeFunctionOnRegion(searchIndexFunction, queryInfo, false);
   }
 
-  protected ResultCollector<?, ?> executeFunction(Function function,
+  protected ResultCollector<?, ?> executeFunctionOnRegion(Function function,
       LuceneFunctionSerializable functionArguments, boolean returnAllMembers) {
     Set<DistributedMember> targetMembers = 
CliUtil.getMembersForeRegionViaFunction(getCache(),
         functionArguments.getRegionPath(), returnAllMembers);
@@ -517,6 +578,11 @@ public class LuceneIndexCommands extends 
AbstractCommandsSupport {
           
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULDNOT_FIND_MEMBERS_FOR_REGION_0,
           new Object[] {functionArguments.getRegionPath()}));
     }
+    return executeFunction(function, functionArguments, targetMembers);
+  }
+
+  protected ResultCollector<?, ?> executeFunction(Function function,
+      LuceneFunctionSerializable functionArguments, Set<DistributedMember> 
targetMembers) {
     return CliUtil.executeFunction(function, functionArguments, targetMembers);
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
index f1b9c82..f7edd8f 100644
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
@@ -55,7 +55,7 @@ public class LuceneCreateIndexFunction extends 
FunctionAdapter implements Intern
   }
 
   public String getId() {
-    return LuceneListIndexFunction.class.getName();
+    return LuceneCreateIndexFunction.class.getName();
   }
 
   public void execute(final FunctionContext context) {

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java
 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java
index 1535637..e4e2277 100644
--- 
a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java
+++ 
b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java
@@ -20,7 +20,9 @@ import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.cache.lucene.LuceneService;
 import org.apache.geode.cache.lucene.LuceneServiceProvider;
-import org.apache.geode.cache.lucene.internal.cli.LuceneIndexInfo;
+import org.apache.geode.cache.lucene.internal.LuceneServiceImpl;
+import org.apache.geode.cache.lucene.internal.cli.LuceneDestroyIndexInfo;
+import org.apache.geode.cache.lucene.internal.xml.LuceneXmlConstants;
 import org.apache.geode.internal.InternalEntity;
 import org.apache.geode.internal.cache.xmlcache.CacheXml;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
@@ -29,26 +31,39 @@ import 
org.apache.geode.management.internal.configuration.domain.XmlEntity;
 public class LuceneDestroyIndexFunction implements Function, InternalEntity {
 
   public void execute(final FunctionContext context) {
+    CliFunctionResult result = null;
     String memberId = 
getCache().getDistributedSystem().getDistributedMember().getId();
     try {
-      LuceneIndexInfo indexInfo = (LuceneIndexInfo) context.getArguments();
+      LuceneDestroyIndexInfo indexInfo = (LuceneDestroyIndexInfo) 
context.getArguments();
       String indexName = indexInfo.getIndexName();
       String regionPath = indexInfo.getRegionPath();
       LuceneService service = LuceneServiceProvider.get(getCache());
       if (indexName == null) {
-        service.destroyIndexes(regionPath);
+        if (indexInfo.isDefinedDestroyOnly()) {
+          ((LuceneServiceImpl) service).destroyDefinedIndexes(regionPath);
+          result = new CliFunctionResult(memberId);
+        } else {
+          service.destroyIndexes(regionPath);
+          result = new CliFunctionResult(memberId, getXmlEntity(indexName, 
regionPath));
+        }
       } else {
-        service.destroyIndex(indexName, regionPath);
+        if (indexInfo.isDefinedDestroyOnly()) {
+          ((LuceneServiceImpl) service).destroyDefinedIndex(indexName, 
regionPath);
+          result = new CliFunctionResult(memberId);
+        } else {
+          service.destroyIndex(indexName, regionPath);
+          result = new CliFunctionResult(memberId, getXmlEntity(indexName, 
regionPath));
+        }
       }
-      context.getResultSender()
-          .lastResult(new CliFunctionResult(memberId, 
getXmlEntity(regionPath)));
     } catch (Exception e) {
-      context.getResultSender().lastResult(new CliFunctionResult(memberId, e, 
e.getMessage()));
+      result = new CliFunctionResult(memberId, e, e.getMessage());
     }
+    context.getResultSender().lastResult(result);
   }
 
-  protected XmlEntity getXmlEntity(String regionPath) {
-    return new XmlEntity(CacheXml.REGION, "name", regionPath);
+  protected XmlEntity getXmlEntity(String indexName, String regionPath) {
+    return new XmlEntity(CacheXml.REGION, "name", regionPath, 
LuceneXmlConstants.PREFIX,
+        LuceneXmlConstants.NAMESPACE, LuceneXmlConstants.INDEX, "name", 
indexName);
   }
 
   protected Cache getCache() {

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java
 
b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java
index a3e2a90..7f203ce 100755
--- 
a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java
+++ 
b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.cache.lucene.internal.cli;
 
+import junitparams.Parameters;
 import org.apache.geode.cache.*;
 import org.apache.geode.cache.lucene.LuceneIndex;
 import org.apache.geode.cache.lucene.LuceneQuery;
@@ -22,7 +23,7 @@ import org.apache.geode.cache.lucene.LuceneServiceProvider;
 import org.apache.geode.cache.lucene.internal.LuceneIndexCreationProfile;
 import org.apache.geode.cache.lucene.internal.LuceneIndexImpl;
 import org.apache.geode.cache.lucene.internal.LuceneServiceImpl;
-import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.management.cli.Result.Status;
 import org.apache.geode.management.internal.cli.CommandManager;
 import org.apache.geode.management.internal.cli.commands.CliCommandTestBase;
@@ -38,6 +39,7 @@ import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.core.KeywordAnalyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -51,7 +53,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 import junitparams.JUnitParamsRunner;
@@ -203,47 +204,6 @@ public class LuceneIndexCommandsDUnitTest extends 
CliCommandTestBase {
   }
 
   @Test
-  public void createIndexOnGroupShouldCreateANewIndexOnGroup() throws 
Exception {
-    final VM vm1 = Host.getHost(0).getVM(1);
-    final VM vm2 = Host.getHost(0).getVM(2);
-    vm1.invoke(() -> {
-      getCache();
-    });
-    vm2.invoke(() -> {
-      Properties props = new Properties();
-      props.setProperty(ConfigurationProperties.GROUPS, "group1");
-      getSystem(props);
-      getCache();
-    });
-
-    CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance());
-
-    CommandStringBuilder csb = new 
CommandStringBuilder(LuceneCliStrings.LUCENE_CREATE_INDEX);
-    csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME, INDEX_NAME);
-    csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME);
-    csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD, 
"field1,field2,field3");
-    csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP, "group1");
-    String resultAsString = executeCommandAndLogResult(csb);
-
-    vm2.invoke(() -> {
-      LuceneService luceneService = LuceneServiceProvider.get(getCache());
-      createRegion();
-      final LuceneIndex index = luceneService.getIndex(INDEX_NAME, 
REGION_NAME);
-      assertArrayEquals(new String[] {"field1", "field2", "field3"}, 
index.getFieldNames());
-    });
-
-    vm1.invoke(() -> {
-      LuceneService luceneService = LuceneServiceProvider.get(getCache());
-      try {
-        createRegion();
-        fail("Should have thrown an exception due to the missing index");
-      } catch (IllegalStateException expected) {
-
-      }
-    });
-  }
-
-  @Test
   public void createIndexWithoutRegionShouldReturnCorrectResults() throws 
Exception {
     final VM vm1 = Host.getHost(0).getVM(1);
     vm1.invoke(() -> {
@@ -543,42 +503,70 @@ public class LuceneIndexCommandsDUnitTest extends 
CliCommandTestBase {
   }
 
   @Test
-  public void destroySingleIndexOnRegion() throws Exception {
+  @Parameters({"true", "false"})
+  public void testDestroySingleIndex(boolean createRegion) throws Exception {
     final VM vm1 = Host.getHost(0).getVM(1);
-    createIndex(vm1);
-    CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance());
-    CommandStringBuilder csb = new 
CommandStringBuilder(LuceneCliStrings.LUCENE_DESTROY_INDEX);
-    csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME, INDEX_NAME);
-    csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME);
-    String resultAsString = executeCommandAndLogResult(csb);
+    if (createRegion) {
+      createIndex(vm1);
+    } else {
+      createIndexWithoutRegion(vm1);
+    }
+    CommandResult result = createAndExecuteDestroyIndexCommand(INDEX_NAME, 
REGION_NAME);
+    String resultAsString = commandResultToString(result);
     String expectedStatus = CliStrings.format(
-        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FOR_REGION_1,
+        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1,
         new Object[] {INDEX_NAME, REGION_NAME});
     assertTrue(resultAsString.contains(expectedStatus));
   }
 
   @Test
-  public void destroyAllIndexesOnRegion() throws Exception {
+  @Parameters({"true", "false"})
+  public void testDestroyAllIndexes(boolean createRegion) throws Exception {
     final VM vm1 = Host.getHost(0).getVM(1);
-    createIndex(vm1);
-    CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance());
-    CommandStringBuilder csb = new 
CommandStringBuilder(LuceneCliStrings.LUCENE_DESTROY_INDEX);
-    csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME);
-    String resultAsString = executeCommandAndLogResult(csb);
+    if (createRegion) {
+      createIndex(vm1);
+    } else {
+      createIndexWithoutRegion(vm1);
+    }
+    CommandResult result = createAndExecuteDestroyIndexCommand(null, 
REGION_NAME);
+    String resultAsString = commandResultToString(result);
     String expectedStatus = CliStrings.format(
-        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FOR_REGION_0,
+        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0,
         new Object[] {REGION_NAME});
     assertTrue(resultAsString.contains(expectedStatus));
   }
 
   @Test
-  public void destroyIndexWithoutRegionShouldReturnError() throws Exception {
+  public void testDestroyNonExistentSingleIndex() throws Exception {
     final VM vm1 = Host.getHost(0).getVM(1);
-    createIndexWithoutRegion(vm1);
+    vm1.invoke(() -> createRegion());
+    CommandResult result = createAndExecuteDestroyIndexCommand(INDEX_NAME, 
REGION_NAME);
+    String resultAsString = commandResultToString(result);
+    String expectedStatus = 
LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1
+        .toLocalizedString(new Object[] {INDEX_NAME, '/' + REGION_NAME});
+    assertTrue(resultAsString.contains(expectedStatus));
+  }
+
+  @Test
+  public void testDestroyNonExistentIndexes() throws Exception {
+    final VM vm1 = Host.getHost(0).getVM(1);
+    vm1.invoke(() -> createRegion());
+    CommandResult result = createAndExecuteDestroyIndexCommand(null, 
REGION_NAME);
+    String resultAsString = commandResultToString(result);
+    String expectedStatus = 
LocalizedStrings.LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0
+        .toLocalizedString(new Object[] {'/' + REGION_NAME});
+    assertTrue(resultAsString.contains(expectedStatus));
+  }
+
+  private CommandResult createAndExecuteDestroyIndexCommand(String indexName, 
String regionPath)
+      throws Exception {
+    CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance());
     CommandStringBuilder csb = new 
CommandStringBuilder(LuceneCliStrings.LUCENE_DESTROY_INDEX);
-    csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME);
-    String resultAsString = executeCommandAndLogResult(csb);
-    
assertTrue(resultAsString.contains(getRegionNotFoundErrorMessage(REGION_NAME)));
+    if (indexName != null) {
+      csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME, indexName);
+    }
+    csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, regionPath);
+    return executeCommandAndGetResult(csb);
   }
 
   private void createRegion() {

http://git-wip-us.apache.org/repos/asf/geode/blob/834235a5/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java
----------------------------------------------------------------------
diff --git 
a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java
 
b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java
index 9e8d7a9..e4b4b4f 100644
--- 
a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java
+++ 
b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java
@@ -27,6 +27,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.core.KeywordAnalyzer;
@@ -34,6 +36,7 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
 import org.apache.geode.cache.Cache;
@@ -71,6 +74,7 @@ import org.apache.geode.test.junit.categories.UnitTest;
  * @since GemFire 7.0
  */
 @Category(UnitTest.class)
+@RunWith(JUnitParamsRunner.class)
 public class LuceneIndexCommandsJUnitTest {
 
   @Test
@@ -185,8 +189,8 @@ public class LuceneIndexCommandsJUnitTest {
     cliFunctionResults.add(new CliFunctionResult("member2", false, "Index 
creation failed"));
     cliFunctionResults.add(new CliFunctionResult("member3", true, "Index 
Created"));
 
-    doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(
-        isA(LuceneCreateIndexFunction.class), any(), 
any(LuceneIndexInfo.class));
+    doReturn(mockResultCollector).when(commands).executeFunctionOnAllMembers(
+        isA(LuceneCreateIndexFunction.class), any(LuceneIndexInfo.class));
     doReturn(cliFunctionResults).when(mockResultCollector).getResult();
 
     String indexName = "index";
@@ -196,7 +200,7 @@ public class LuceneIndexCommandsJUnitTest {
         KeywordAnalyzer.class.getCanonicalName(), 
StandardAnalyzer.class.getCanonicalName()};
 
     CommandResult result = (CommandResult) commands.createIndex(indexName, 
regionPath,
-        searchableFields, fieldAnalyzers, null);
+        searchableFields, fieldAnalyzers);
     assertEquals(Status.OK, result.getStatus());
     TabularResultData data = (TabularResultData) result.getResultData();
     assertEquals(Arrays.asList("member1", "member2", "member3"), 
data.retrieveAllValues("Member"));
@@ -223,8 +227,8 @@ public class LuceneIndexCommandsJUnitTest {
     indexDetails.add(createIndexDetails("memberFive", "/Employees", 
searchableFields,
         fieldAnalyzers, mockIndexStats, true, serverName));
 
-    doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(
-        isA(LuceneDescribeIndexFunction.class), any(), 
any(LuceneIndexInfo.class));
+    doReturn(mockResultCollector).when(commands).executeFunctionOnRegion(
+        isA(LuceneDescribeIndexFunction.class), any(LuceneIndexInfo.class), 
eq(true));
     doReturn(indexDetails).when(mockResultCollector).getResult();
 
     CommandResult result = (CommandResult) 
commands.describeIndex("memberFive", "/Employees");
@@ -404,29 +408,141 @@ public class LuceneIndexCommandsJUnitTest {
   }
 
   @Test
-  public void testDestroySingleIndexOnRegion() throws Exception {
+  @Parameters({"true", "false"})
+  public void testDestroySingleIndexNoRegionMembers(boolean expectedToSucceed) 
throws Exception {
     LuceneIndexCommands commands = 
createTestLuceneIndexCommandsForDestroyIndex();
     String indexName = "index";
     String regionPath = "regionPath";
+
+    final ResultCollector mockResultCollector = mock(ResultCollector.class);
+    final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
+    String expectedStatus = null;
+    if (expectedToSucceed) {
+      expectedStatus = CliStrings.format(
+          
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1,
+          new Object[] {indexName, regionPath});
+      cliFunctionResults.add(new CliFunctionResult("member0"));
+    } else {
+      Exception e = new IllegalStateException("failed");
+      expectedStatus = e.getMessage();
+      cliFunctionResults.add(new CliFunctionResult("member0", e, 
e.getMessage()));
+    }
+
+    doReturn(mockResultCollector).when(commands).executeFunction(
+        isA(LuceneDestroyIndexFunction.class), 
any(LuceneDestroyIndexInfo.class), any());
+    doReturn(cliFunctionResults).when(mockResultCollector).getResult();
+
+    doReturn(Collections.emptySet()).when(commands).getNormalMembers(any());
+    doReturn(Collections.emptySet()).when(commands).getRegionMembers(any(), 
any());
+
+    CommandResult result = (CommandResult) commands.destroyIndex(indexName, 
regionPath);
+    verifyDestroyIndexCommandResult(result, cliFunctionResults, 
expectedStatus);
+  }
+
+  @Test
+  @Parameters({"true", "false"})
+  public void testDestroySingleIndexWithRegionMembers(boolean 
expectedToSucceed) throws Exception {
+    LuceneIndexCommands commands = 
createTestLuceneIndexCommandsForDestroyIndex();
+    String indexName = "index";
+    String regionPath = "regionPath";
+
+    Set<DistributedMember> members = new HashSet<>();
+    DistributedMember mockMember = mock(DistributedMember.class);
+    when(mockMember.getId()).thenReturn("member0");
+    members.add(mockMember);
+
+    final ResultCollector mockResultCollector = mock(ResultCollector.class);
+    final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
+    String expectedStatus = null;
+    if (expectedToSucceed) {
+      expectedStatus = CliStrings.format(
+          
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1,
+          new Object[] {indexName, regionPath});
+      cliFunctionResults.add(new CliFunctionResult(mockMember.getId()));
+    } else {
+      Exception e = new IllegalStateException("failed");
+      expectedStatus = e.getMessage();
+      cliFunctionResults.add(new CliFunctionResult("member0", e, 
e.getMessage()));
+    }
+
+    doReturn(mockResultCollector).when(commands).executeFunction(
+        isA(LuceneDestroyIndexFunction.class), 
any(LuceneDestroyIndexInfo.class), any());
+    doReturn(cliFunctionResults).when(mockResultCollector).getResult();
+
+    doReturn(members).when(commands).getNormalMembers(any());
+    doReturn(members).when(commands).getRegionMembers(any(), any());
+
     CommandResult result = (CommandResult) commands.destroyIndex(indexName, 
regionPath);
-    String expectedMember = "member";
-    String expectedStatus = CliStrings.format(
-        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FOR_REGION_1,
-        new Object[] {indexName, regionPath});
-    verifyDestroyIndexCommandResult(result, expectedMember, expectedStatus);
+    verifyDestroyIndexCommandResult(result, cliFunctionResults, 
expectedStatus);
   }
 
   @Test
-  public void testDestroyAllIndexesOnRegion() throws Exception {
+  @Parameters({"true", "false"})
+  public void testDestroyAllIndexesNoRegionMembers(boolean expectedToSucceed) 
throws Exception {
     LuceneIndexCommands commands = 
createTestLuceneIndexCommandsForDestroyIndex();
     String indexName = null;
     String regionPath = "regionPath";
+
+    final ResultCollector mockResultCollector = mock(ResultCollector.class);
+    final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
+    String expectedStatus = null;
+    if (expectedToSucceed) {
+      expectedStatus = CliStrings.format(
+          
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0,
+          new Object[] {regionPath});
+      cliFunctionResults.add(new CliFunctionResult("member0"));
+    } else {
+      Exception e = new IllegalStateException("failed");
+      expectedStatus = e.getMessage();
+      cliFunctionResults.add(new CliFunctionResult("member0", e, 
e.getMessage()));
+    }
+
+    doReturn(mockResultCollector).when(commands).executeFunction(
+        isA(LuceneDestroyIndexFunction.class), 
any(LuceneDestroyIndexInfo.class), any());
+    doReturn(cliFunctionResults).when(mockResultCollector).getResult();
+
+    doReturn(Collections.emptySet()).when(commands).getNormalMembers(any());
+    doReturn(Collections.emptySet()).when(commands).getRegionMembers(any(), 
any());
+
     CommandResult result = (CommandResult) commands.destroyIndex(indexName, 
regionPath);
-    String expectedMember = "member";
-    String expectedStatus = CliStrings.format(
-        
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FOR_REGION_0,
-        new Object[] {regionPath});
-    verifyDestroyIndexCommandResult(result, expectedMember, expectedStatus);
+    verifyDestroyIndexCommandResult(result, cliFunctionResults, 
expectedStatus);
+  }
+
+  @Test
+  @Parameters({"true", "false"})
+  public void testDestroyAllIndexesWithRegionMembers(boolean 
expectedToSucceed) throws Exception {
+    LuceneIndexCommands commands = 
createTestLuceneIndexCommandsForDestroyIndex();
+    String indexName = null;
+    String regionPath = "regionPath";
+
+    Set<DistributedMember> members = new HashSet<>();
+    DistributedMember mockMember = mock(DistributedMember.class);
+    when(mockMember.getId()).thenReturn("member0");
+    members.add(mockMember);
+
+    final ResultCollector mockResultCollector = mock(ResultCollector.class);
+    final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
+    String expectedStatus = null;
+    if (expectedToSucceed) {
+      expectedStatus = CliStrings.format(
+          
LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0,
+          new Object[] {regionPath});
+      cliFunctionResults.add(new CliFunctionResult(mockMember.getId()));
+    } else {
+      Exception e = new IllegalStateException("failed");
+      expectedStatus = e.getMessage();
+      cliFunctionResults.add(new CliFunctionResult("member0", e, 
e.getMessage()));
+    }
+
+    doReturn(mockResultCollector).when(commands).executeFunction(
+        isA(LuceneDestroyIndexFunction.class), 
any(LuceneDestroyIndexInfo.class), any());
+    doReturn(cliFunctionResults).when(mockResultCollector).getResult();
+
+    doReturn(Collections.emptySet()).when(commands).getNormalMembers(any());
+    doReturn(Collections.emptySet()).when(commands).getRegionMembers(any(), 
any());
+
+    CommandResult result = (CommandResult) commands.destroyIndex(indexName, 
regionPath);
+    verifyDestroyIndexCommandResult(result, cliFunctionResults, 
expectedStatus);
   }
 
   private LuceneIndexCommands createTestLuceneIndexCommandsForDestroyIndex() {
@@ -437,21 +553,27 @@ public class LuceneIndexCommandsJUnitTest {
     final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
     cliFunctionResults.add(new CliFunctionResult("member", true, "Index 
Destroyed"));
 
-    doReturn(mockResultCollector).when(commands).executeFunction(
+    doReturn(mockResultCollector).when(commands).executeFunctionOnRegion(
         isA(LuceneDestroyIndexFunction.class), any(LuceneIndexInfo.class), 
eq(false));
     doReturn(cliFunctionResults).when(mockResultCollector).getResult();
     return commands;
   }
 
-  private void verifyDestroyIndexCommandResult(CommandResult result, String 
expectedMember,
-      String expectedStatus) {
+  private void verifyDestroyIndexCommandResult(CommandResult result,
+      List<CliFunctionResult> cliFunctionResults, String expectedStatus) {
     assertEquals(Status.OK, result.getStatus());
     TabularResultData data = (TabularResultData) result.getResultData();
     List<String> members = data.retrieveAllValues("Member");
+    assertEquals(cliFunctionResults.size(), members.size());
+    // Verify each member
+    for (int i = 0; i < members.size(); i++) {
+      assertEquals("member" + i, members.get(i));
+    }
+    // Verify each status
     List<String> status = data.retrieveAllValues("Status");
-    assertTrue(members.size() == 1);
-    assertEquals(expectedMember, members.get(0));
-    assertEquals(expectedStatus, status.get(0));
+    for (int i = 0; i < status.size(); i++) {
+      assertEquals(expectedStatus, status.get(i));
+    }
   }
 
   private String getPage(final LuceneSearchResults[] expectedResults, int[] 
indexList) {

Reply via email to