/* * 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 java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.geode.InternalGemFireError; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.asyncqueue.AsyncEventQueue; import org.apache.geode.cache.asyncqueue.internal.AsyncEventQueueFactoryImpl; import org.apache.geode.cache.asyncqueue.internal.AsyncEventQueueImpl; import org.apache.geode.cache.lucene.internal.repository.RepositoryManager; import org.apache.geode.cache.lucene.internal.xml.LuceneIndexCreation; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.InternalRegionArguments; import org.apache.geode.internal.cache.LocalRegion; import org.apache.geode.internal.cache.PartitionedRegion; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; public abstract class LuceneIndexImpl implements InternalLuceneIndex { protected static final Logger logger = LogService.getLogger(); protected final String indexName; protected final String regionPath; protected final Cache cache; protected final LuceneIndexStats indexStats; protected boolean hasInitialized = false; protected Map<String, Analyzer> fieldAnalyzers; protected String[] searchableFieldNames; protected RepositoryManager repositoryManager; protected Analyzer analyzer; protected LocalRegion dataRegion; protected LuceneIndexImpl(String indexName, String regionPath, Cache cache) { this.indexName = indexName; this.regionPath = regionPath; this.cache = cache; final String statsName = indexName + "-" + regionPath; this.indexStats = new LuceneIndexStats(cache.getDistributedSystem(), statsName); } @Override public String getName() { return this.indexName; } @Override public String getRegionPath() { return this.regionPath; } protected LocalRegion getDataRegion() { return (LocalRegion) cache.getRegion(regionPath); } protected boolean withPersistence() { RegionAttributes ra = dataRegion.getAttributes(); DataPolicy dp = ra.getDataPolicy(); final boolean withPersistence = dp.withPersistence(); return withPersistence; } protected void setSearchableFields(String[] fields) { searchableFieldNames = fields; } @Override public boolean waitUntilFlushed(long timeout, TimeUnit unit) throws InterruptedException { String aeqId = LuceneServiceImpl.getUniqueIndexName(indexName, regionPath); AsyncEventQueueImpl queue = (AsyncEventQueueImpl) cache.getAsyncEventQueue(aeqId); if (queue != null) { return queue.waitUntilFlushed(timeout, unit); } else { throw new IllegalArgumentException( "The AEQ does not exist for the index " + indexName + " region " + regionPath); } } @Override public String[] getFieldNames() { return searchableFieldNames; } @Override public Map<String, Analyzer> getFieldAnalyzers() { return this.fieldAnalyzers; } public RepositoryManager getRepositoryManager() { return this.repositoryManager; } public void setAnalyzer(Analyzer analyzer) { if (analyzer == null) { this.analyzer = new StandardAnalyzer(); } else { this.analyzer = analyzer; } } public Analyzer getAnalyzer() { return this.analyzer; } public Cache getCache() { return this.cache; } public void setFieldAnalyzers(Map<String, Analyzer> fieldAnalyzers) { this.fieldAnalyzers = fieldAnalyzers == null ? null : Collections.unmodifiableMap(fieldAnalyzers); } public LuceneIndexStats getIndexStats() { return indexStats; } protected void initialize() { if (!hasInitialized) { /* create index region */ dataRegion = getDataRegion(); // assert dataRegion != null; repositoryManager = createRepositoryManager(); // create AEQ, AEQ listener and specify the listener to repositoryManager createAEQ(dataRegion); addExtension(dataRegion); hasInitialized = true; } } protected abstract RepositoryManager createRepositoryManager(); protected AsyncEventQueue createAEQ(Region dataRegion) { return createAEQ(createAEQFactory(dataRegion)); } private AsyncEventQueueFactoryImpl createAEQFactory(final Region dataRegion) { AsyncEventQueueFactoryImpl factory = (AsyncEventQueueFactoryImpl) cache.createAsyncEventQueueFactory(); if (dataRegion instanceof PartitionedRegion) { factory.setParallel(true); // parallel AEQ for PR } else { factory.setParallel(false); // TODO: not sure if serial AEQ working or not } factory.setMaximumQueueMemory(1000); factory.setDispatcherThreads(10); factory.setIsMetaQueue(true); if (dataRegion.getAttributes().getDataPolicy().withPersistence()) { factory.setPersistent(true); } factory.setDiskStoreName(dataRegion.getAttributes().getDiskStoreName()); factory.setDiskSynchronous(dataRegion.getAttributes().isDiskSynchronous()); factory.setForwardExpirationDestroy(true); return factory; } private AsyncEventQueue createAEQ(AsyncEventQueueFactoryImpl factory) { LuceneEventListener listener = new LuceneEventListener(repositoryManager); String aeqId = LuceneServiceImpl.getUniqueIndexName(getName(), regionPath); AsyncEventQueue indexQueue = factory.create(aeqId, listener); return indexQueue; } /** * Register an extension with the region so that xml will be generated for this index. */ protected void addExtension(LocalRegion dataRegion) { LuceneIndexCreation creation = new LuceneIndexCreation(); creation.setName(this.getName()); creation.addFieldNames(this.getFieldNames()); creation.setRegion(dataRegion); creation.setFieldAnalyzers(this.getFieldAnalyzers()); dataRegion.getExtensionPoint().addExtension(creation); } 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 // (among other things) InternalRegionArguments ira = new InternalRegionArguments().setDestroyLockFlag(true).setRecreateFlag(false) .setSnapshotInputStream(null).setImageTarget(null).setIsUsedForMetaRegion(true); // Create the region try { return ((GemFireCacheImpl) this.cache).createVMRegion(regionName, attributes, ira); } catch (Exception e) { InternalGemFireError ige = new InternalGemFireError( LocalizedStrings.GemFireCache_UNEXPECTED_EXCEPTION.toLocalizedString()); ige.initCause(e); throw ige; } } }