GEODE-2404: Added support for destroying lucene indexes
Project: http://git-wip-us.apache.org/repos/asf/geode/repo Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/11521a82 Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/11521a82 Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/11521a82 Branch: refs/heads/GEODE-4160-mockito Commit: 11521a824f31ff03db13d0e59cb0fbf29e592151 Parents: c4a5ab2 Author: Barry Oglesby <[email protected]> Authored: Mon Feb 13 17:01:13 2017 -0800 Committer: Barry Oglesby <[email protected]> Committed: Mon Feb 27 10:06:48 2017 -0800 ---------------------------------------------------------------------- .../internal/AsyncEventQueueImpl.java | 9 + .../geode/internal/DataSerializableFixedID.java | 2 +- .../geode/internal/i18n/LocalizedStrings.java | 4 + .../geode/cache/lucene/LuceneService.java | 14 +- .../internal/DestroyLuceneIndexMessage.java | 109 ++++++++ .../lucene/internal/InternalLuceneIndex.java | 5 + .../LuceneIndexForPartitionedRegion.java | 109 +++++++- .../cache/lucene/internal/LuceneIndexImpl.java | 16 ++ .../cache/lucene/internal/LuceneRawIndex.java | 2 + .../lucene/internal/LuceneServiceImpl.java | 57 ++++- .../lucene/internal/cli/LuceneCliStrings.java | 28 ++- .../cli/LuceneFunctionSerializable.java | 36 +++ .../internal/cli/LuceneIndexCommands.java | 113 +++++++-- .../lucene/internal/cli/LuceneIndexDetails.java | 16 +- .../lucene/internal/cli/LuceneIndexInfo.java | 15 +- .../lucene/internal/cli/LuceneQueryInfo.java | 15 +- .../functions/LuceneDestroyIndexFunction.java | 57 +++++ .../lucene/LuceneIndexDestroyDUnitTest.java | 247 +++++++++++++++++++ .../cli/LuceneIndexCommandsDUnitTest.java | 50 +++- .../cli/LuceneIndexCommandsJUnitTest.java | 53 ++++ .../LuceneDestroyIndexFunctionJUnitTest.java | 93 +++++++ .../LuceneClusterConfigurationDUnitTest.java | 99 +++++++- 22 files changed, 1068 insertions(+), 81 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-core/src/main/java/org/apache/geode/cache/asyncqueue/internal/AsyncEventQueueImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/cache/asyncqueue/internal/AsyncEventQueueImpl.java b/geode-core/src/main/java/org/apache/geode/cache/asyncqueue/internal/AsyncEventQueueImpl.java index 3b99f1c..a44b9e4 100644 --- a/geode-core/src/main/java/org/apache/geode/cache/asyncqueue/internal/AsyncEventQueueImpl.java +++ b/geode-core/src/main/java/org/apache/geode/cache/asyncqueue/internal/AsyncEventQueueImpl.java @@ -25,6 +25,7 @@ import org.apache.geode.cache.wan.GatewayEventFilter; import org.apache.geode.cache.wan.GatewayEventSubstitutionFilter; import org.apache.geode.cache.wan.GatewaySender; import org.apache.geode.cache.wan.GatewaySender.OrderPolicy; +import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.RegionQueue; import org.apache.geode.internal.cache.wan.AbstractGatewaySender; import org.apache.geode.internal.cache.wan.AbstractGatewaySenderEventProcessor; @@ -191,8 +192,16 @@ public class AsyncEventQueueImpl implements AsyncEventQueue { return ((AbstractGatewaySender) sender).getIsMetaQueue(); } + public void stop() { + if (this.sender.isRunning()) { + this.sender.stop(); + } + } + public void destroy() { + GemFireCacheImpl gfci = (GemFireCacheImpl) ((AbstractGatewaySender) this.sender).getCache(); this.sender.destroy(); + gfci.removeAsyncEventQueue(this); } public boolean isBucketSorted() { http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-core/src/main/java/org/apache/geode/internal/DataSerializableFixedID.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/DataSerializableFixedID.java b/geode-core/src/main/java/org/apache/geode/internal/DataSerializableFixedID.java index 74e40e9..4e45646 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/DataSerializableFixedID.java +++ b/geode-core/src/main/java/org/apache/geode/internal/DataSerializableFixedID.java @@ -809,8 +809,8 @@ public interface DataSerializableFixedID extends SerializationVersions { public static final short LUCENE_ENTRY_SCORE = 2174; public static final short LUCENE_TOP_ENTRIES = 2175; public static final short LUCENE_TOP_ENTRIES_COLLECTOR = 2176; - public static final short WAIT_UNTIL_FLUSHED_FUNCTION_CONTEXT = 2177; + public static final short DESTROY_LUCENE_INDEX_MESSAGE = 2178; // NOTE, codes > 65535 will take 4 bytes to serialize http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 d855e29..fa63437 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 @@ -7674,6 +7674,10 @@ public class LocalizedStrings { new StringId(6650, "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}."); /** Testing strings, messageId 90000-99999 **/ /** http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneService.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneService.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneService.java index d273760..5cfae59 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneService.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneService.java @@ -101,10 +101,18 @@ public interface LuceneService { /** * Destroy the lucene index - * - * @param index index object + * + * @param indexName the name of the index to destroy + * @param regionPath the path of the region whose index to destroy + */ + public void destroyIndex(String indexName, String regionPath); + + /** + * Destroy all the lucene indexes for the region + * + * @param regionPath The path of the region on which to destroy the indexes */ - public void destroyIndex(LuceneIndex index); + public void destroyIndexes(String regionPath); /** * Get the lucene index object specified by region name and index name http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java new file mode 100644 index 0000000..8bdef9b --- /dev/null +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java @@ -0,0 +1,109 @@ +/* + * 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.DataSerializer; +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.lucene.LuceneServiceProvider; +import org.apache.geode.distributed.internal.*; +import org.apache.geode.internal.cache.GemFireCacheImpl; +import org.apache.geode.internal.logging.LogService; +import org.apache.logging.log4j.Logger; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Collection; + +public class DestroyLuceneIndexMessage extends PooledDistributionMessage + implements MessageWithReply { + + private int processorId; + + private String regionPath; + + private String indexName; + + private static final Logger logger = LogService.getLogger(); + + /* For serialization */ + public DestroyLuceneIndexMessage() {} + + protected DestroyLuceneIndexMessage(Collection recipients, int processorId, String regionPath, + String indexName) { + super(); + setRecipients(recipients); + this.processorId = processorId; + this.regionPath = regionPath; + this.indexName = indexName; + } + + @Override + protected void process(DistributionManager dm) { + ReplyException replyException = null; + try { + if (logger.isDebugEnabled()) { + logger.debug("DestroyLuceneIndexMessage: Destroying regionPath=" + this.regionPath + + "; indexName=" + this.indexName); + } + try { + Cache cache = GemFireCacheImpl.getInstance(); + LuceneServiceImpl impl = (LuceneServiceImpl) LuceneServiceProvider.get(cache); + impl.destroyIndex(this.indexName, this.regionPath, false); + if (logger.isDebugEnabled()) { + logger.debug("DestroyLuceneIndexMessage: Destroyed regionPath=" + this.regionPath + + "; indexName=" + this.indexName); + } + } catch (Throwable e) { + replyException = new ReplyException(e); + if (logger.isDebugEnabled()) { + logger.debug( + "DestroyLuceneIndexMessage: Caught the following exception attempting to destroy indexName=" + + this.indexName + "; regionPath=" + this.regionPath + ":", + e); + } + } + } finally { + ReplyMessage replyMsg = new ReplyMessage(); + replyMsg.setRecipient(getSender()); + replyMsg.setProcessorId(this.processorId); + if (replyException != null) { + replyMsg.setException(replyException); + } + dm.putOutgoing(replyMsg); + } + } + + @Override + public int getDSFID() { + return DESTROY_LUCENE_INDEX_MESSAGE; + } + + @Override + public void toData(DataOutput out) throws IOException { + super.toData(out); + out.writeInt(this.processorId); + DataSerializer.writeString(this.regionPath, out); + DataSerializer.writeString(this.indexName, out); + } + + @Override + public void fromData(DataInput in) throws IOException, ClassNotFoundException { + super.fromData(in); + this.processorId = in.readInt(); + this.regionPath = DataSerializer.readString(in); + this.indexName = DataSerializer.readString(in); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/InternalLuceneIndex.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/InternalLuceneIndex.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/InternalLuceneIndex.java index 2800f49..c28fcdc 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/InternalLuceneIndex.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/InternalLuceneIndex.java @@ -27,4 +27,9 @@ public interface InternalLuceneIndex extends LuceneIndex { */ public void dumpFiles(String directory); + /** + * Destroy the index + */ + public void destroy(boolean initiator); + } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java index 9a39b39..f45d94d 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java @@ -15,6 +15,7 @@ package org.apache.geode.cache.lucene.internal; +import org.apache.geode.CancelException; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.FixedPartitionResolver; @@ -24,6 +25,7 @@ import org.apache.geode.cache.PartitionResolver; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.asyncqueue.internal.AsyncEventQueueImpl; import org.apache.geode.cache.execute.FunctionService; import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.cache.lucene.internal.directory.DumpDirectoryFiles; @@ -36,15 +38,23 @@ import org.apache.geode.cache.lucene.internal.repository.RepositoryManager; import org.apache.geode.cache.lucene.internal.repository.serializer.HeterogeneousLuceneSerializer; import org.apache.geode.cache.partition.PartitionListener; import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.ReplyException; +import org.apache.geode.distributed.internal.ReplyProcessor21; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.PartitionedRegion; +import java.util.Set; + /* wrapper of IndexWriter */ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { protected Region<String, File> fileRegion; protected Region<ChunkKey, byte[]> chunkRegion; protected final FileSystemStats fileSystemStats; + public static final String FILES_REGION_SUFFIX = ".files"; + public static final String CHUNKS_REGION_SUFFIX = ".chunks"; + public LuceneIndexForPartitionedRegion(String indexName, String regionPath, Cache cache) { super(indexName, regionPath, cache); @@ -123,7 +133,7 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { } public String createFileRegionName() { - return LuceneServiceImpl.getUniqueIndexName(indexName, regionPath) + ".files"; + return LuceneServiceImpl.getUniqueIndexRegionName(indexName, regionPath, FILES_REGION_SUFFIX); } boolean chunkRegionExists(String chunkRegionName) { @@ -139,7 +149,7 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { } public String createChunkRegionName() { - return LuceneServiceImpl.getUniqueIndexName(indexName, regionPath) + ".chunks"; + return LuceneServiceImpl.getUniqueIndexRegionName(indexName, regionPath, CHUNKS_REGION_SUFFIX); } private PartitionAttributesFactory configureLuceneRegionAttributesFactory( @@ -192,4 +202,99 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { .withArgs(new String[] {directory, indexName}).execute(DumpDirectoryFiles.ID); results.getResult(); } + + @Override + public void destroy(boolean initiator) { + if (logger.isDebugEnabled()) { + logger.debug("Destroying index regionPath=" + regionPath + "; indexName=" + indexName + + "; initiator=" + initiator); + } + + // Invoke super destroy to remove the extension + super.destroy(initiator); + + // Destroy the AsyncEventQueue + PartitionedRegion pr = (PartitionedRegion) getDataRegion(); + destroyAsyncEventQueue(pr); + + // Destroy the chunk region (colocated with the file region) + // localDestroyRegion can't be used because locally destroying regions is not supported on + // colocated regions + if (!chunkRegion.isDestroyed()) { + chunkRegion.destroyRegion(); + if (logger.isDebugEnabled()) { + logger.debug("Destroyed chunkRegion=" + chunkRegion.getName()); + } + } + + // Destroy the file region (colocated with the application region) + // localDestroyRegion can't be used because locally destroying regions is not supported on + // colocated regions + if (!fileRegion.isDestroyed()) { + fileRegion.destroyRegion(); + if (logger.isDebugEnabled()) { + logger.debug("Destroyed fileRegion=" + fileRegion.getName()); + } + } + + // Destroy index on remote members if necessary + if (initiator) { + destroyOnRemoteMembers(pr); + } + + if (logger.isDebugEnabled()) { + logger.debug("Destroyed index regionPath=" + regionPath + "; indexName=" + indexName + + "; initiator=" + initiator); + } + } + + private void destroyAsyncEventQueue(PartitionedRegion pr) { + String aeqId = LuceneServiceImpl.getUniqueIndexName(indexName, regionPath); + + // Get the AsyncEventQueue + AsyncEventQueueImpl aeq = (AsyncEventQueueImpl) cache.getAsyncEventQueue(aeqId); + + // Stop the AsyncEventQueue (this stops the AsyncEventQueue's underlying GatewaySender) + aeq.stop(); + + // Remove the id from the dataRegion's AsyncEventQueue ids + // Note: The region may already have been destroyed by a remote member + if (!pr.isDestroyed()) { + pr.getAttributesMutator().removeAsyncEventQueueId(aeqId); + } + + // Destroy the aeq (this also removes it from the GemFireCacheImpl) + aeq.destroy(); + if (logger.isDebugEnabled()) { + logger.debug("Destroyed aeqId=" + aeqId); + } + } + + private void destroyOnRemoteMembers(PartitionedRegion pr) { + DM dm = pr.getDistributionManager(); + Set<InternalDistributedMember> recipients = pr.getRegionAdvisor().adviseDataStore(); + if (!recipients.isEmpty()) { + if (logger.isDebugEnabled()) { + logger.debug("LuceneIndexForPartitionedRegion: About to send destroy message recipients=" + + recipients); + } + ReplyProcessor21 processor = new ReplyProcessor21(dm, recipients); + DestroyLuceneIndexMessage message = new DestroyLuceneIndexMessage(recipients, + processor.getProcessorId(), regionPath, indexName); + dm.putOutgoing(message); + if (logger.isDebugEnabled()) { + logger.debug("LuceneIndexForPartitionedRegion: Sent message recipients=" + recipients); + } + try { + processor.waitForReplies(); + } catch (ReplyException e) { + if (!(e.getCause() instanceof CancelException)) { + throw e; + } + } catch (InterruptedException e) { + dm.getCancelCriterion().checkCancelInProgress(e); + Thread.currentThread().interrupt(); + } + } + } } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java index 42ccc84..cf519be 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import org.apache.geode.internal.cache.extension.Extension; import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; @@ -203,6 +204,21 @@ public abstract class LuceneIndexImpl implements InternalLuceneIndex { dataRegion.getExtensionPoint().addExtension(creation); } + public void destroy(boolean initiator) { + // Find and delete the appropriate extension + Extension extensionToDelete = null; + for (Extension extension : getDataRegion().getExtensionPoint().getExtensions()) { + LuceneIndexCreation index = (LuceneIndexCreation) extension; + if (index.getName().equals(indexName)) { + extensionToDelete = extension; + break; + } + } + if (extensionToDelete != null) { + getDataRegion().getExtensionPoint().removeExtension(extensionToDelete); + } + } + protected <K, V> Region<K, V> createRegion(final String regionName, final RegionAttributes<K, V> attributes) { // Create InternalRegionArguments to set isUsedForMetaRegion true to suppress xml generation http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRawIndex.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRawIndex.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRawIndex.java index a4e48ad..f4518aa 100755 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRawIndex.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRawIndex.java @@ -36,4 +36,6 @@ public class LuceneRawIndex extends LuceneIndexImpl { return; } + @Override + public void destroy(boolean initiator) {} } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 cf7b2c9..a608dd9 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 @@ -114,6 +114,11 @@ public class LuceneServiceImpl implements InternalLuceneService { return name; } + public static String getUniqueIndexRegionName(String indexName, String regionPath, + String regionSuffix) { + return getUniqueIndexName(indexName, regionPath) + regionSuffix; + } + @Override public void createIndex(String indexName, String regionPath, String... fields) { if (fields == null || fields.length == 0) { @@ -256,10 +261,53 @@ public class LuceneServiceImpl implements InternalLuceneService { } @Override - public void destroyIndex(LuceneIndex index) { - LuceneIndexImpl indexImpl = (LuceneIndexImpl) index; + public void destroyIndex(String indexName, String regionPath) { + destroyIndex(indexName, regionPath, true); + } + + protected void destroyIndex(String indexName, String regionPath, boolean initiator) { + if (!regionPath.startsWith("/")) { + regionPath = "/" + regionPath; + } + LuceneIndexImpl indexImpl = (LuceneIndexImpl) getIndex(indexName, regionPath); + if (indexImpl == null) { + throw new IllegalArgumentException( + LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1.toLocalizedString(indexName, + regionPath)); + } else { + indexImpl.destroy(initiator); + removeFromIndexMap(indexImpl); + logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_REGION_1 + .toLocalizedString(indexName, regionPath)); + } + } + + @Override + public void destroyIndexes(String regionPath) { + destroyIndexes(regionPath, true); + } + + protected void destroyIndexes(String regionPath, boolean initiator) { + if (!regionPath.startsWith("/")) { + regionPath = "/" + regionPath; + } + List<LuceneIndexImpl> indexesToDestroy = new ArrayList<>(); + for (LuceneIndex index : getAllIndexes()) { + if (index.getRegionPath().equals(regionPath)) { + LuceneIndexImpl indexImpl = (LuceneIndexImpl) index; + indexImpl.destroy(initiator); + indexesToDestroy.add(indexImpl); + } + } + for (LuceneIndex index : indexesToDestroy) { + removeFromIndexMap(index); + logger.info(LocalizedStrings.LuceneService_DESTROYED_INDEX_0_FROM_REGION_1 + .toLocalizedString(index.getName(), regionPath)); + } + } + + private void removeFromIndexMap(LuceneIndex index) { indexMap.remove(getUniqueIndexName(index.getName(), index.getRegionPath())); - // indexImpl.close(); } @Override @@ -320,6 +368,9 @@ public class LuceneServiceImpl implements InternalLuceneService { DSFIDFactory.registerDSFID(DataSerializableFixedID.WAIT_UNTIL_FLUSHED_FUNCTION_CONTEXT, WaitUntilFlushedFunctionContext.class); + + DSFIDFactory.registerDSFID(DataSerializableFixedID.DESTROY_LUCENE_INDEX_MESSAGE, + DestroyLuceneIndexMessage.class); } public Collection<LuceneIndexCreationProfile> getAllDefinedIndexes() { http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 36f5aeb..fbb70d2 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 @@ -37,7 +37,7 @@ public class LuceneCliStrings { public static final String LUCENE_CREATE_INDEX__NAME__HELP = "Name of the lucene index to create."; public static final String LUCENE_CREATE_INDEX__REGION_HELP = - "Name/Path of the region where the lucene index is created on."; + "Name/Path of the region on which to create the lucene index."; public static final String LUCENE_CREATE_INDEX__FIELD = "field"; public static final String LUCENE_CREATE_INDEX__FIELD_HELP = "fields on the region values which are stored in the lucene index."; @@ -61,14 +61,13 @@ public class LuceneCliStrings { // Describe lucene index commands public static final String LUCENE_DESCRIBE_INDEX = "describe lucene index"; public static final String LUCENE_DESCRIBE_INDEX__HELP = - "Display the describe of lucene indexes created for all members."; + "Display the description of lucene indexes created for all members."; public static final String LUCENE_DESCRIBE_INDEX__ERROR_MESSAGE = "An error occurred while collecting lucene index information across the Geode cluster: %1$s"; public static final String LUCENE_DESCRIBE_INDEX__NAME__HELP = "Name of the lucene index to describe."; public static final String LUCENE_DESCRIBE_INDEX__REGION_HELP = - "Name/Path of the region where the lucene index to be described exists."; - + "Name/Path of the region defining the lucene index to be described."; // Search lucene index commands public static final String LUCENE_SEARCH_INDEX = "search lucene"; @@ -78,7 +77,7 @@ public class LuceneCliStrings { public static final String LUCENE_SEARCH_INDEX__NAME__HELP = "Name of the lucene index to search."; public static final String LUCENE_SEARCH_INDEX__REGION_HELP = - "Name/Path of the region where the lucene index exists."; + "Name/Path of the region defining the lucene index to be searched."; public static final String LUCENE_SEARCH_INDEX__QUERY_STRING = "queryStrings"; public static final String LUCENE_SEARCH_INDEX__LIMIT = "limit"; public static final String LUCENE_SEARCH_INDEX__LIMIT__HELP = "Number of search results needed"; @@ -95,4 +94,23 @@ public class LuceneCliStrings { public static final String LUCENE_SEARCH_INDEX__KEYSONLY__HELP = "Return only keys of search results."; + // Destroy lucene index command + public static final String LUCENE_DESTROY_INDEX = "destroy lucene index"; + public static final String LUCENE_DESTROY_INDEX__HELP = "Destroy the lucene index."; + public static final String LUCENE_DESTROY_INDEX__EXCEPTION_MESSAGE = + "An unexpected exception occurred while destroying lucene index:"; + public static final String LUCENE_DESTROY_INDEX__NAME__HELP = + "Name of the lucene index to destroy."; + public static final String LUCENE_DESTROY_INDEX__REGION_HELP = + "Name of the region defining the lucene index to be destroyed."; + public static final String LUCENE_DESTROY_INDEX__MSG__REGION_CANNOT_BE_EMPTY = + "Region cannot be empty."; + public static final String LUCENE_DESTROY_INDEX__MSG__INDEX_CANNOT_BE_EMPTY = + "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}"; } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneFunctionSerializable.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneFunctionSerializable.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneFunctionSerializable.java new file mode 100644 index 0000000..1390e22 --- /dev/null +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneFunctionSerializable.java @@ -0,0 +1,36 @@ +/* + * 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; + +import java.io.Serializable; + +public class LuceneFunctionSerializable implements Serializable { + + protected final String indexName; + protected final String regionPath; + + public LuceneFunctionSerializable(final String indexName, final String regionPath) { + this.indexName = indexName; + this.regionPath = regionPath; + } + + public String getIndexName() { + return indexName; + } + + public String getRegionPath() { + return regionPath; + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 b84246c..e2d85a6 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 @@ -16,16 +16,12 @@ package org.apache.geode.cache.lucene.internal.cli; import org.apache.geode.SystemFailure; import org.apache.geode.cache.Cache; -import org.apache.geode.cache.execute.Execution; -import org.apache.geode.cache.execute.FunctionAdapter; -import org.apache.geode.cache.execute.FunctionInvocationTargetException; -import org.apache.geode.cache.execute.ResultCollector; -import org.apache.geode.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction; -import org.apache.geode.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction; -import org.apache.geode.cache.lucene.internal.cli.functions.LuceneListIndexFunction; -import org.apache.geode.cache.lucene.internal.cli.functions.LuceneSearchIndexFunction; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.execute.*; +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.security.IntegratedSecurityService; import org.apache.geode.internal.security.SecurityService; import org.apache.geode.management.cli.CliMetaData; @@ -70,6 +66,8 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { new LuceneDescribeIndexFunction(); private static final LuceneSearchIndexFunction searchIndexFunction = new LuceneSearchIndexFunction(); + private static final LuceneDestroyIndexFunction destroyIndexFunction = + new LuceneDestroyIndexFunction(); private List<LuceneSearchResults> searchResults = null; private SecurityService securityService = IntegratedSecurityService.getSecurityService(); @@ -316,6 +314,74 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { } } + @CliCommand(value = LuceneCliStrings.LUCENE_DESTROY_INDEX, + 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, + + @CliOption(key = LuceneCliStrings.LUCENE__REGION_PATH, mandatory = true, + optionContext = ConverterHint.REGIONPATH, + help = LuceneCliStrings.LUCENE_DESTROY_INDEX__REGION_HELP) final String regionPath) { + if (StringUtils.isBlank(regionPath) || regionPath.equals(Region.SEPARATOR)) { + return ResultBuilder.createInfoResult( + CliStrings.format(LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__REGION_CANNOT_BE_EMPTY)); + } + + if (StringUtils.isEmpty(indexName)) { + return ResultBuilder.createInfoResult( + CliStrings.format(LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__INDEX_CANNOT_BE_EMPTY)); + } + + this.securityService.authorizeRegionManage(regionPath); + + Result result = null; + 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()) { + persistClusterConfiguration(result, () -> { + // Update the xml entity (region entity) to remove the async event id(s) and index(es) + getSharedConfiguration().addXmlEntity((XmlEntity) cliFunctionResult.getXmlEntity(), null); + }); + } + } catch (FunctionInvocationTargetException ignore) { + result = ResultBuilder.createGemFireErrorResult(CliStrings.format( + CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN, LuceneCliStrings.LUCENE_DESTROY_INDEX)); + } catch (VirtualMachineError e) { + SystemFailure.initiateFailure(e); + throw e; + } catch (IllegalArgumentException e) { + result = ResultBuilder.createInfoResult(e.getMessage()); + } catch (Throwable t) { + SystemFailure.checkFailure(); + getCache().getLogger().warning(LuceneCliStrings.LUCENE_DESTROY_INDEX__EXCEPTION_MESSAGE, t); + result = ResultBuilder.createGemFireErrorResult(t.getMessage()); + } + return result; + } + private Result displayResults(int pageSize, boolean keysOnly) throws Exception { if (searchResults.size() == 0) { return ResultBuilder @@ -428,25 +494,30 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { protected ResultCollector<?, ?> executeFunctionOnGroups(FunctionAdapter function, String[] groups, final LuceneIndexInfo indexInfo) throws IllegalArgumentException, CommandResultException { - final Set<DistributedMember> targetMembers; + ResultCollector<?, ?> results = null; if (function != createIndexFunction) { - targetMembers = - CliUtil.getMembersForeRegionViaFunction(getCache(), indexInfo.getRegionPath(), true); - if (targetMembers.isEmpty()) { - throw new IllegalArgumentException("Region not found."); - } + results = executeFunction(function, indexInfo, true); } else { - targetMembers = CliUtil.findMembersOrThrow(groups, null); + Set<DistributedMember> targetMembers = CliUtil.findMembersOrThrow(groups, null); + results = CliUtil.executeFunction(function, indexInfo, targetMembers); } - return CliUtil.executeFunction(function, indexInfo, targetMembers); + return results; } protected ResultCollector<?, ?> executeSearch(final LuceneQueryInfo queryInfo) throws Exception { - final Set<DistributedMember> targetMembers = - CliUtil.getMembersForeRegionViaFunction(getCache(), queryInfo.getRegionPath(), false); - if (targetMembers.isEmpty()) - throw new IllegalArgumentException("Region not found."); - return CliUtil.executeFunction(searchIndexFunction, queryInfo, targetMembers); + return executeFunction(searchIndexFunction, queryInfo, false); + } + + protected ResultCollector<?, ?> executeFunction(Function function, + LuceneFunctionSerializable functionArguments, boolean returnAllMembers) { + Set<DistributedMember> targetMembers = CliUtil.getMembersForeRegionViaFunction(getCache(), + functionArguments.getRegionPath(), returnAllMembers); + if (targetMembers.isEmpty()) { + throw new IllegalArgumentException(CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULDNOT_FIND_MEMBERS_FOR_REGION_0, + new Object[] {functionArguments.getRegionPath()})); + } + return CliUtil.executeFunction(function, functionArguments, targetMembers); } @CliAvailabilityIndicator({LuceneCliStrings.LUCENE_SEARCH_INDEX, http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java index 02a19a1..b2b0466 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java @@ -29,10 +29,9 @@ import org.apache.geode.cache.lucene.internal.LuceneIndexStats; import org.apache.lucene.analysis.Analyzer; -public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Serializable { +public class LuceneIndexDetails extends LuceneFunctionSerializable + implements Comparable<LuceneIndexDetails> { private static final long serialVersionUID = 1L; - private final String indexName; - private final String regionPath; private final String serverName; private final String[] searchableFieldNames; private Map<String, String> fieldAnalyzers = null; @@ -42,8 +41,7 @@ public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Seria public LuceneIndexDetails(final String indexName, final String regionPath, final String[] searchableFieldNames, final Map<String, Analyzer> fieldAnalyzers, LuceneIndexStats indexStats, boolean initialized, final String serverName) { - this.indexName = indexName; - this.regionPath = regionPath; + super(indexName, regionPath); this.serverName = serverName; this.searchableFieldNames = searchableFieldNames; this.fieldAnalyzers = getFieldAnalyzerStrings(fieldAnalyzers); @@ -141,14 +139,6 @@ public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Seria return initialized; } - public String getIndexName() { - return indexName; - } - - public String getRegionPath() { - return regionPath; - } - private static <T extends Comparable<T>> int compare(final T obj1, final T obj2) { return (obj1 == null && obj2 == null ? 0 : (obj1 == null ? 1 : (obj2 == null ? -1 : obj1.compareTo(obj2)))); http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java index b714ff1..41b066e 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java @@ -24,18 +24,15 @@ import org.apache.geode.cache.lucene.internal.LuceneIndexImpl; import org.apache.lucene.analysis.Analyzer; -public class LuceneIndexInfo implements Serializable { +public class LuceneIndexInfo extends LuceneFunctionSerializable { private static final long serialVersionUID = 1L; - private final String indexName; - private final String regionPath; private final String[] searchableFieldNames; private final String[] fieldAnalyzers; public LuceneIndexInfo(final String indexName, final String regionPath, final String[] searchableFieldNames, String[] fieldAnalyzers) { - this.indexName = indexName; - this.regionPath = regionPath; + super(indexName, regionPath); this.searchableFieldNames = searchableFieldNames; this.fieldAnalyzers = fieldAnalyzers; } @@ -44,14 +41,6 @@ public class LuceneIndexInfo implements Serializable { this(indexName, regionPath, null, null); } - public String getIndexName() { - return indexName; - } - - public String getRegionPath() { - return regionPath; - } - public String[] getSearchableFieldNames() { return searchableFieldNames; } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java index 8d34e2e..e57badb 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java @@ -19,10 +19,8 @@ import java.io.Serializable; import org.apache.geode.cache.lucene.LuceneQueryFactory; -public class LuceneQueryInfo implements Serializable { +public class LuceneQueryInfo extends LuceneFunctionSerializable { private static final long serialVersionUID = 1L; - private String indexName; - private String regionPath; private String queryString; private String defaultField; private int limit; @@ -30,22 +28,13 @@ public class LuceneQueryInfo implements Serializable { public LuceneQueryInfo(final String indexName, final String regionPath, final String queryString, final String defaultField, final int limit, final boolean keysOnly) { - this.indexName = indexName; - this.regionPath = regionPath; + super(indexName, regionPath); this.queryString = queryString; this.defaultField = defaultField; this.limit = limit; this.keysOnly = keysOnly; } - public String getIndexName() { - return indexName; - } - - public String getRegionPath() { - return regionPath; - } - public String getQueryString() { return queryString; } http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 new file mode 100644 index 0000000..1535637 --- /dev/null +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java @@ -0,0 +1,57 @@ +/* + * 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.functions; + +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.CacheFactory; +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.internal.InternalEntity; +import org.apache.geode.internal.cache.xmlcache.CacheXml; +import org.apache.geode.management.internal.cli.functions.CliFunctionResult; +import org.apache.geode.management.internal.configuration.domain.XmlEntity; + +public class LuceneDestroyIndexFunction implements Function, InternalEntity { + + public void execute(final FunctionContext context) { + String memberId = getCache().getDistributedSystem().getDistributedMember().getId(); + try { + LuceneIndexInfo indexInfo = (LuceneIndexInfo) context.getArguments(); + String indexName = indexInfo.getIndexName(); + String regionPath = indexInfo.getRegionPath(); + LuceneService service = LuceneServiceProvider.get(getCache()); + if (indexName == null) { + service.destroyIndexes(regionPath); + } else { + service.destroyIndex(indexName, regionPath); + } + context.getResultSender() + .lastResult(new CliFunctionResult(memberId, getXmlEntity(regionPath))); + } catch (Exception e) { + context.getResultSender().lastResult(new CliFunctionResult(memberId, e, e.getMessage())); + } + } + + protected XmlEntity getXmlEntity(String regionPath) { + return new XmlEntity(CacheXml.REGION, "name", regionPath); + } + + protected Cache getCache() { + return CacheFactory.getAnyInstance(); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java new file mode 100644 index 0000000..6260075 --- /dev/null +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java @@ -0,0 +1,247 @@ +/* + * 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; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.lucene.internal.LuceneIndexForPartitionedRegion; +import org.apache.geode.cache.lucene.internal.LuceneServiceImpl; +import org.apache.geode.cache.lucene.test.TestObject; +import org.apache.geode.internal.cache.LocalRegion; +import org.apache.geode.test.dunit.AsyncInvocation; +import org.apache.geode.test.dunit.SerializableRunnableIF; +import org.apache.geode.test.junit.categories.DistributedTest; +import org.awaitility.Awaitility; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; + +import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.INDEX_NAME; +import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.REGION_NAME; +import static org.apache.geode.internal.Assert.fail; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +@Category(DistributedTest.class) +@RunWith(JUnitParamsRunner.class) +public class LuceneIndexDestroyDUnitTest extends LuceneDUnitTest { + + private volatile boolean STOP_PUTS = false; + + private final Object[] parametersForIndexDestroys() { + String[] destroyDataRegionParameters = {"true", "false"}; + RegionTestableType[] regionTestTypes = getListOfRegionTestTypes(); + return parameterCombiner(destroyDataRegionParameters, regionTestTypes); + } + + @Test + @Parameters(method = "parametersForIndexDestroys") + public void verifyDestroySingleIndex(boolean destroyDataRegion, RegionTestableType regionType) { + // Create index and region + dataStore1.invoke(() -> initDataStore(createIndex(), regionType)); + dataStore2.invoke(() -> initDataStore(createIndex(), regionType)); + + // Verify index created + dataStore1.invoke(() -> verifyIndexCreated()); + dataStore2.invoke(() -> verifyIndexCreated()); + + // Attempt to destroy data region (should fail) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(false)); + } + + // Destroy index (only needs to be done on one member) + dataStore1.invoke(() -> destroyIndex()); + + // Verify index destroyed + dataStore1.invoke(() -> verifyIndexDestroyed()); + dataStore2.invoke(() -> verifyIndexDestroyed()); + + // Attempt to destroy data region (should succeed) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(true)); + } + } + + @Test + @Parameters(method = "parametersForIndexDestroys") + public void verifyDestroyAllIndexes(boolean destroyDataRegion, RegionTestableType regionType) { + // Create indexes and region + dataStore1.invoke(() -> initDataStore(createIndexes(), regionType)); + dataStore2.invoke(() -> initDataStore(createIndexes(), regionType)); + + // Verify indexes created + dataStore1.invoke(() -> verifyIndexesCreated()); + dataStore2.invoke(() -> verifyIndexesCreated()); + + // Attempt to destroy data region (should fail) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(false)); + } + + // Destroy indexes (only needs to be done on one member) + dataStore1.invoke(() -> destroyIndexes()); + + // Verify indexes destroyed + dataStore1.invoke(() -> verifyIndexesDestroyed()); + dataStore2.invoke(() -> verifyIndexesDestroyed()); + + // Attempt to destroy data region (should succeed) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(true)); + } + } + + @Ignore + // Destroying an index while puts are occurring currently fails with a + // GatewaySenderConfigurationException. + @Parameters(method = "getListOfServerRegionTestTypes") + public void verifyDestroySingleIndexWhileDoingPuts(RegionTestableType regionType) + throws Exception { + // Create index and region + dataStore1.invoke(() -> initDataStore(createIndex(), regionType)); + dataStore2.invoke(() -> initDataStore(createIndex(), regionType)); + + // Verify index created + dataStore1.invoke(() -> verifyIndexCreated()); + dataStore2.invoke(() -> verifyIndexCreated()); + + // Start puts + AsyncInvocation putter = dataStore1.invokeAsync(() -> doPuts()); + + // Wait until puts have started + dataStore1.invoke(() -> waitUntilPutsHaveStarted()); + + // Destroy index (only needs to be done on one member) + dataStore1.invoke(() -> destroyIndex()); + + // Verify index destroyed + dataStore1.invoke(() -> verifyIndexDestroyed()); + dataStore2.invoke(() -> verifyIndexDestroyed()); + + // End puts + dataStore1.invoke(() -> stopPuts()); + putter.join(); + } + + + private SerializableRunnableIF createIndex() { + return () -> { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + luceneService.createIndex(INDEX_NAME, REGION_NAME, "text"); + }; + } + + private SerializableRunnableIF createIndexes() { + return () -> { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + luceneService.createIndex(INDEX_NAME + "0", REGION_NAME, "text"); + luceneService.createIndex(INDEX_NAME + "1", REGION_NAME, "text"); + }; + } + + private void verifyIndexCreated() { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + assertNotNull(luceneService.getIndex(INDEX_NAME, REGION_NAME)); + } + + private void verifyIndexesCreated() { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + assertNotNull(luceneService.getIndex(INDEX_NAME + "0", REGION_NAME)); + assertNotNull(luceneService.getIndex(INDEX_NAME + "1", REGION_NAME)); + } + + private void doPuts() throws Exception { + Region region = getCache().getRegion(REGION_NAME); + int i = 0; + while (!STOP_PUTS) { + region.put(i++, new TestObject()); + // Thread.sleep(50); + } + } + + private void stopPuts() { + STOP_PUTS = true; + } + + private void waitUntilPutsHaveStarted() { + Awaitility.waitAtMost(30, TimeUnit.SECONDS) + .until(() -> getCache().getRegion(REGION_NAME).size() > 0); + } + + private void destroyDataRegion(boolean shouldSucceed) { + Region region = getCache().getRegion(REGION_NAME); + assertNotNull(region); + try { + region.destroyRegion(); + if (!shouldSucceed) { + fail("should not have been able to destroy data region named " + region.getFullPath()); + } + } catch (IllegalStateException e) { + if (shouldSucceed) { + fail(e); + } + } + } + + private void destroyIndex() { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + luceneService.destroyIndex(INDEX_NAME, REGION_NAME); + } + + private void destroyIndexes() { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + luceneService.destroyIndexes(REGION_NAME); + } + + private void verifyIndexDestroyed() { + verifyIndexDestroyed(INDEX_NAME); + } + + private void verifyIndexesDestroyed() { + verifyIndexDestroyed(INDEX_NAME + "0"); + verifyIndexDestroyed(INDEX_NAME + "1"); + } + + private void verifyIndexDestroyed(String indexName) { + LuceneService luceneService = LuceneServiceProvider.get(getCache()); + + // Verify the index itself no longer exists + assertNull(luceneService.getIndex(indexName, REGION_NAME)); + + // Verify the underlying files region no longer exists + String filesRegionName = LuceneServiceImpl.getUniqueIndexRegionName(indexName, REGION_NAME, + LuceneIndexForPartitionedRegion.FILES_REGION_SUFFIX); + assertNull(getCache().getRegion(filesRegionName)); + + // Verify the underlying chunks region no longer exists + String chunksRegionName = LuceneServiceImpl.getUniqueIndexRegionName(indexName, REGION_NAME, + LuceneIndexForPartitionedRegion.CHUNKS_REGION_SUFFIX); + assertNull(getCache().getRegion(chunksRegionName)); + + // Verify the underlying AsyncEventQueue no longer exists + String aeqId = LuceneServiceImpl.getUniqueIndexName(indexName, REGION_NAME); + assertNull(getCache().getAsyncEventQueue(aeqId)); + + // Verify the data region extension no longer exists + LocalRegion region = (LocalRegion) getCache().getRegion(REGION_NAME); + assertFalse(region.getExtensionPoint().getExtensions().iterator().hasNext()); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 49aa057..efc11ab 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 @@ -28,6 +28,7 @@ import org.apache.geode.internal.lang.StringUtils; 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; +import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.result.CommandResult; import org.apache.geode.management.internal.cli.result.TabularResultData; import org.apache.geode.management.internal.cli.util.CommandStringBuilder; @@ -349,7 +350,7 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME, "notAnIndex"); csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME); String resultAsString = executeCommandAndLogResult(csb); - assertTrue(resultAsString.contains("Region not found")); + assertTrue(resultAsString.contains(getRegionNotFoundErrorMessage(REGION_NAME))); } @Test @@ -498,7 +499,7 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { csb.addOption(LuceneCliStrings.LUCENE_SEARCH_INDEX__DEFAULT_FIELD, "field2"); String resultAsString = executeCommandAndLogResult(csb); - assertTrue(resultAsString.contains("Region not found")); + assertTrue(resultAsString.contains(getRegionNotFoundErrorMessage(REGION_NAME))); } @Test @@ -552,6 +553,45 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { } + @Test + public void destroySingleIndexOnRegion() 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); + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FOR_REGION_1, + new Object[] {INDEX_NAME, REGION_NAME}); + assertTrue(resultAsString.contains(expectedStatus)); + } + + @Test + public void destroyAllIndexesOnRegion() 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); + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FOR_REGION_0, + new Object[] {REGION_NAME}); + assertTrue(resultAsString.contains(expectedStatus)); + } + + @Test + public void destroyIndexWithoutRegionShouldReturnError() throws Exception { + final VM vm1 = Host.getHost(0).getVM(1); + createIndexWithoutRegion(vm1); + 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))); + } + private void createRegion() { getCache().createRegionFactory(RegionShortcut.PARTITION).create(REGION_NAME); } @@ -628,6 +668,12 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { }); } + private String getRegionNotFoundErrorMessage(String regionPath) { + return CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULDNOT_FIND_MEMBERS_FOR_REGION_0, + new Object[] {regionPath}); + } + protected class TestObject implements Serializable { private String field1; private String field2; http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/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 6ddef9e..9e8d7a9 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,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.core.KeywordAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; @@ -41,6 +42,7 @@ import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.cache.lucene.internal.LuceneIndexStats; import org.apache.geode.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction; import org.apache.geode.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction; +import org.apache.geode.cache.lucene.internal.cli.functions.LuceneDestroyIndexFunction; import org.apache.geode.cache.lucene.internal.cli.functions.LuceneListIndexFunction; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.internal.cache.execute.AbstractExecution; @@ -401,6 +403,57 @@ public class LuceneIndexCommandsJUnitTest { assertEquals(queryResults.size(), data.retrieveAllValues("key").size()); } + @Test + public void testDestroySingleIndexOnRegion() throws Exception { + LuceneIndexCommands commands = createTestLuceneIndexCommandsForDestroyIndex(); + String indexName = "index"; + String regionPath = "regionPath"; + 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); + } + + @Test + public void testDestroyAllIndexesOnRegion() throws Exception { + LuceneIndexCommands commands = createTestLuceneIndexCommandsForDestroyIndex(); + String indexName = null; + String regionPath = "regionPath"; + 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); + } + + private LuceneIndexCommands createTestLuceneIndexCommandsForDestroyIndex() { + final Cache mockCache = mock(Cache.class); + final ResultCollector mockResultCollector = mock(ResultCollector.class); + final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null)); + + final List<CliFunctionResult> cliFunctionResults = new ArrayList<>(); + cliFunctionResults.add(new CliFunctionResult("member", true, "Index Destroyed")); + + doReturn(mockResultCollector).when(commands).executeFunction( + isA(LuceneDestroyIndexFunction.class), any(LuceneIndexInfo.class), eq(false)); + doReturn(cliFunctionResults).when(mockResultCollector).getResult(); + return commands; + } + + private void verifyDestroyIndexCommandResult(CommandResult result, String expectedMember, + String expectedStatus) { + assertEquals(Status.OK, result.getStatus()); + TabularResultData data = (TabularResultData) result.getResultData(); + List<String> members = data.retrieveAllValues("Member"); + List<String> status = data.retrieveAllValues("Status"); + assertTrue(members.size() == 1); + assertEquals(expectedMember, members.get(0)); + assertEquals(expectedStatus, status.get(0)); + } + private String getPage(final LuceneSearchResults[] expectedResults, int[] indexList) { final TabularResultData data = ResultBuilder.createTabularResultData(); for (int i : indexList) { http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java new file mode 100644 index 0000000..f86f4a1 --- /dev/null +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java @@ -0,0 +1,93 @@ +/* + * 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.functions; + +import org.apache.geode.cache.execute.FunctionContext; +import org.apache.geode.cache.execute.ResultSender; +import org.apache.geode.cache.lucene.internal.InternalLuceneService; +import org.apache.geode.cache.lucene.internal.cli.LuceneIndexInfo; +import org.apache.geode.internal.cache.GemFireCacheImpl; +import org.apache.geode.management.internal.cli.functions.CliFunctionResult; +import org.apache.geode.management.internal.configuration.domain.XmlEntity; +import org.apache.geode.test.fake.Fakes; +import org.apache.geode.test.junit.categories.UnitTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.mockito.ArgumentCaptor; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +@Category(UnitTest.class) +public class LuceneDestroyIndexFunctionJUnitTest { + + private InternalLuceneService service; + private GemFireCacheImpl cache; + private String member; + private FunctionContext context; + private ResultSender resultSender; + + @Before + public void prepare() { + this.cache = Fakes.cache(); + this.member = Fakes.distributedSystem().getDistributedMember().getId(); + this.service = mock(InternalLuceneService.class); + when(this.cache.getService(InternalLuceneService.class)).thenReturn(this.service); + this.context = mock(FunctionContext.class); + this.resultSender = mock(ResultSender.class); + when(this.context.getResultSender()).thenReturn(this.resultSender); + } + + @Test + @SuppressWarnings("unchecked") + public void testExecuteWithRegionAndIndex() throws Throwable { + LuceneIndexInfo indexInfo = new LuceneIndexInfo("index1", "/region1"); + when(this.context.getArguments()).thenReturn(indexInfo); + LuceneDestroyIndexFunction function = new LuceneDestroyIndexFunction(); + function = spy(function); + doReturn(this.cache).when(function).getCache(); + doReturn(mock(XmlEntity.class)).when(function).getXmlEntity(any()); + function.execute(this.context); + verify(this.service).destroyIndex(eq("index1"), eq("/region1")); + verify(this.service, never()).destroyIndexes(eq("/region1")); + ArgumentCaptor<CliFunctionResult> resultCaptor = + ArgumentCaptor.forClass(CliFunctionResult.class); + verify(resultSender).lastResult(resultCaptor.capture()); + CliFunctionResult result = resultCaptor.getValue(); + assertEquals(this.member, result.getMemberIdOrName()); + assertEquals(true, result.isSuccessful()); + } + + @Test + @SuppressWarnings("unchecked") + public void testExecuteWithRegion() throws Throwable { + LuceneIndexInfo indexInfo = new LuceneIndexInfo(null, "/region1"); + when(this.context.getArguments()).thenReturn(indexInfo); + LuceneDestroyIndexFunction function = new LuceneDestroyIndexFunction(); + function = spy(function); + doReturn(this.cache).when(function).getCache(); + doReturn(mock(XmlEntity.class)).when(function).getXmlEntity(any()); + function.execute(this.context); + verify(this.service).destroyIndexes(eq("/region1")); + verify(this.service, never()).destroyIndex(any(), eq("/region1")); + ArgumentCaptor<CliFunctionResult> resultCaptor = + ArgumentCaptor.forClass(CliFunctionResult.class); + verify(resultSender).lastResult(resultCaptor.capture()); + CliFunctionResult result = resultCaptor.getValue(); + assertEquals(this.member, result.getMemberIdOrName()); + assertEquals(true, result.isSuccessful()); + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/11521a82/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/configuration/LuceneClusterConfigurationDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/configuration/LuceneClusterConfigurationDUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/configuration/LuceneClusterConfigurationDUnitTest.java index 875422c..1a344db 100755 --- a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/configuration/LuceneClusterConfigurationDUnitTest.java +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/configuration/LuceneClusterConfigurationDUnitTest.java @@ -17,10 +17,7 @@ package org.apache.geode.cache.lucene.internal.configuration; import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.INDEX_NAME; import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.REGION_NAME; import static org.apache.geode.distributed.ConfigurationProperties.GROUPS; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.lucene.LuceneIndex; @@ -28,9 +25,14 @@ import org.apache.geode.cache.lucene.LuceneService; import org.apache.geode.cache.lucene.LuceneServiceProvider; import org.apache.geode.cache.lucene.internal.cli.LuceneCliStrings; import org.apache.geode.cache.lucene.internal.cli.LuceneIndexCommands; +import org.apache.geode.cache.lucene.internal.xml.LuceneXmlConstants; +import org.apache.geode.distributed.internal.ClusterConfigurationService; +import org.apache.geode.distributed.internal.InternalLocator; import org.apache.geode.management.internal.cli.CommandManager; import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.util.CommandStringBuilder; +import org.apache.geode.management.internal.configuration.domain.Configuration; +import org.apache.geode.test.dunit.SerializableRunnableIF; import org.apache.geode.test.dunit.rules.GfshShellConnectionRule; import org.apache.geode.test.dunit.rules.Locator; import org.apache.geode.test.dunit.rules.LocatorServerStartupRule; @@ -231,6 +233,78 @@ public class LuceneClusterConfigurationDUnitTest { }); } + @Test + public void verifyClusterConfigurationAfterDestroyIndex() throws Exception { + Member vm1 = startNodeUsingClusterConfiguration(1, false); + + // Connect Gfsh to locator. + gfshConnector.connectAndVerify(locator); + + // Create and add indexes + createAndAddIndexes(); + + // Destroy one index + destroyLuceneIndexUsingGfsh(INDEX_NAME + "0"); + + // Destroy other index + destroyLuceneIndexUsingGfsh(INDEX_NAME + "1"); + + // Verify cluster configuration no longer contains any indexes + locator.invoke(verifyClusterConfiguration(false)); + } + + @Test + public void verifyClusterConfigurationAfterDestroyIndexes() throws Exception { + Member vm1 = startNodeUsingClusterConfiguration(1, false); + + // Connect Gfsh to locator. + gfshConnector.connectAndVerify(locator); + + // Create and add indexes + createAndAddIndexes(); + + // Destroy all indexes + destroyLuceneIndexUsingGfsh(null); + + // Verify cluster configuration no longer contains indexes + locator.invoke(verifyClusterConfiguration(false)); + } + + private void createAndAddIndexes() throws Exception { + // Create lucene index. + createLuceneIndexUsingGfsh(INDEX_NAME + "0", false); + + // Create another lucene index. + createLuceneIndexUsingGfsh(INDEX_NAME + "1", false); + + // Create region + createRegionUsingGfsh(REGION_NAME, RegionShortcut.PARTITION, null); + + // Verify cluster configuration contains the indexes + locator.invoke(verifyClusterConfiguration(true)); + } + + private SerializableRunnableIF verifyClusterConfiguration(boolean verifyIndexesExist) { + return () -> { + InternalLocator internalLocator = LocatorServerStartupRule.locatorStarter.locator; + ClusterConfigurationService sc = internalLocator.getSharedConfiguration(); + Configuration config = sc.getConfiguration(ClusterConfigurationService.CLUSTER_CONFIG); + String xmlContent = config.getCacheXmlContent(); + String luceneIndex0Config = "<" + LuceneXmlConstants.PREFIX + ":" + LuceneXmlConstants.INDEX + + " xmlns:lucene=\"" + LuceneXmlConstants.NAMESPACE + "\" " + LuceneXmlConstants.NAME + + "=\"" + INDEX_NAME + "0" + "\">"; + String luceneIndex1Config = "<" + LuceneXmlConstants.PREFIX + ":" + LuceneXmlConstants.INDEX + + " xmlns:lucene=\"" + LuceneXmlConstants.NAMESPACE + "\" " + LuceneXmlConstants.NAME + + "=\"" + INDEX_NAME + "1" + "\">"; + if (verifyIndexesExist) { + assertTrue(xmlContent.contains(luceneIndex0Config)); + assertTrue(xmlContent.contains(luceneIndex1Config)); + } else { + assertFalse(xmlContent.contains(luceneIndex0Config)); + assertFalse(xmlContent.contains(luceneIndex1Config)); + } + }; + } private Member startNodeUsingClusterConfiguration(int vmIndex, boolean addGroup) throws Exception { @@ -242,10 +316,14 @@ public class LuceneClusterConfigurationDUnitTest { } private void createLuceneIndexUsingGfsh(boolean addGroup) throws Exception { + createLuceneIndexUsingGfsh(INDEX_NAME, addGroup); + } + + private void createLuceneIndexUsingGfsh(String indexName, boolean addGroup) throws Exception { // Execute Gfsh command to create lucene index. 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__INDEX_NAME, indexName); csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME); if (addGroup) { csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP, groupName); @@ -273,6 +351,17 @@ public class LuceneClusterConfigurationDUnitTest { gfshConnector.executeAndVerifyCommand(csb.toString()); } + private void destroyLuceneIndexUsingGfsh(String indexName) throws Exception { + // Execute Gfsh command to destroy lucene index. + CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); + CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_DESTROY_INDEX); + if (indexName != null) { + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME, indexName); + } + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH, REGION_NAME); + gfshConnector.executeAndVerifyCommand(csb.toString()); + } + private void createRegionUsingGfsh(String regionName, RegionShortcut regionShortCut, String group) throws Exception { CommandStringBuilder csb = new CommandStringBuilder(CliStrings.CREATE_REGION);
