/* * ModeShape (http://www.modeshape.org) * * Licensed 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.modeshape.jcr.index.lucene; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.codecs.Codec; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.search.SearcherManager; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.LockFactory; import org.apache.lucene.store.MMapDirectory; import org.apache.lucene.store.NIOFSDirectory; import org.apache.lucene.store.NativeFSLockFactory; import org.apache.lucene.store.NoLockFactory; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.store.SimpleFSLockFactory; import org.modeshape.common.annotation.Immutable; import org.modeshape.common.annotation.ThreadSafe; import org.modeshape.common.util.CheckArg; import org.modeshape.common.util.Reflection; import org.modeshape.common.util.StringUtil; import org.modeshape.jcr.Environment; /** * Holder of various Lucene configuration options. * * @author Horia Chiorean (hchiorea@redhat.com) * @since 4.5 */ @Immutable @ThreadSafe public final class LuceneConfig { protected static final String LAST_SUCCESSFUL_COMMIT_TIME = "last_commit_time"; private final LockFactory lockFactory; private final String directoryClass; private final Analyzer analyzer; private final Codec codec; private final String basePath; private final AtomicLong lastSuccessfulCommitTime; protected static LuceneConfig inMemory() { return new LuceneConfig(null, null, null, null, null, null); } protected static LuceneConfig onDisk(String baseDir) { return new LuceneConfig(baseDir, null, null, null, null, null); } protected LuceneConfig(String baseDir, String lockFactoryClass, String directoryClass, String analyzerClass, String codecName, Environment environment) { this.directoryClass = directoryClass; this.lockFactory = lockFactory(lockFactoryClass); this.analyzer = analyzer(analyzerClass, environment); this.codec = codec(codecName); this.basePath = baseDir; this.lastSuccessfulCommitTime = new AtomicLong(-1); } protected IndexWriter newWriter( String workspaceName, String indexName ) { CheckArg.isNotNull(indexName, "indexName"); CheckArg.isNotNull(workspaceName, "workspaceName"); try { Directory directory = directory(directoryClass, workspaceName, indexName); IndexWriter indexWriter = new IndexWriter(directory, newIndexWriterConfig()); if (DirectoryReader.indexExists(directory)) { readLatestCommitTime(indexWriter); } return indexWriter; } catch (IOException e) { throw new LuceneIndexException("Cannot create index writer", e); } } private IndexWriterConfig newIndexWriterConfig() { IndexWriterConfig writerConfig = new IndexWriterConfig(analyzer); writerConfig.setCommitOnClose(true); writerConfig.setCodec(codec); return writerConfig; } protected SearcherManager searchManager( IndexWriter writer ) { try { return new SearcherManager(writer, true, true, null); } catch (IOException e) { throw new LuceneIndexException("Cannot create index writer", e); } } protected long lastSuccessfulCommitTime() { return lastSuccessfulCommitTime.get(); } protected int refreshTimeSeconds() { return 30; } /** * Returns the analyzer configured for Lucene. * * @return a {@link Analyzer} instance; never null */ public Analyzer getAnalyzer() { return analyzer; } private void readLatestCommitTime( IndexWriter writer ) { Map<String, String> commitData = writer.getCommitData(); String timestampString = commitData.get(LAST_SUCCESSFUL_COMMIT_TIME); if (timestampString == null) { return; } long timestamp = Long.valueOf(timestampString); long currentTs = lastSuccessfulCommitTime.get(); if (timestamp > currentTs) { lastSuccessfulCommitTime.compareAndSet(currentTs, timestamp); } } private Codec codec(String name) { return StringUtil.isBlank(name) ? Codec.getDefault() : Codec.forName(name); } private LockFactory lockFactory(String lockFactoryClass) { if (StringUtil.isBlank(lockFactoryClass)) { return null; } switch (lockFactoryClass) { case "org.apache.lucene.store.NativeFSLockFactory" : { return NativeFSLockFactory.INSTANCE; } case "org.apache.lucene.store.NoLockFactory" : { return NoLockFactory.INSTANCE; } case "org.apache.lucene.store.SimpleFSLockFactory" : { return SimpleFSLockFactory.INSTANCE; } default: throw new IllegalArgumentException("Unknown lock factory implementation: " + lockFactoryClass); } } private Analyzer analyzer(String analyzerClass, Environment environment) { if (StringUtil.isBlank(analyzerClass)) { // we don't want any stop words by default return new StandardAnalyzer(CharArraySet.EMPTY_SET); } else { return Reflection.getInstance(analyzerClass, environment.getClassLoader(this)); } } private Directory directory( String directoryClass, String workspaceName, String indexName ) throws IOException { boolean useLockFactory = lockFactory != null; if (StringUtil.isBlank(basePath)) { return useLockFactory ? new RAMDirectory(lockFactory) : new RAMDirectory(); } Path path = Paths.get(basePath, workspaceName, indexName); if (StringUtil.isBlank(directoryClass)) { return useLockFactory ? FSDirectory.open(path, lockFactory) : FSDirectory.open(path); } switch (directoryClass) { case "org.apache.lucene.store.RAMDirectory" : { return useLockFactory ? new RAMDirectory(lockFactory) : new RAMDirectory(); } case "org.apache.lucene.store.MMapDirectory" : { return useLockFactory ? new MMapDirectory(path, lockFactory) : new MMapDirectory(path); } case "org.apache.lucene.store.NIOFSDirectory" : { return useLockFactory ? new NIOFSDirectory(path, lockFactory) : new NIOFSDirectory(path); } case "org.apache.lucene.store.SimpleFSDirectory" : { return useLockFactory ? new SimpleFSDirectory(path, lockFactory) : new SimpleFSDirectory(path); } default: { throw new IllegalArgumentException("Unknown Lucene directory: " + directoryClass); } } } }