multiindex.java

来自「jsr170接口的java实现。是个apache的开源项目。」· Java 代码 · 共 1,855 行 · 第 1/5 页

JAVA
1,855
字号
/* * 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.jackrabbit.core.query.lucene;import org.apache.jackrabbit.core.NodeId;import org.apache.jackrabbit.core.fs.FileSystemException;import org.apache.jackrabbit.core.fs.local.LocalFileSystem;import org.apache.jackrabbit.core.state.ItemStateException;import org.apache.jackrabbit.core.state.ItemStateManager;import org.apache.jackrabbit.core.state.NoSuchItemStateException;import org.apache.jackrabbit.core.state.NodeState;import org.apache.jackrabbit.uuid.Constants;import org.apache.jackrabbit.uuid.UUID;import org.apache.jackrabbit.util.Timer;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.apache.lucene.document.Document;import org.apache.lucene.index.IndexReader;import org.apache.lucene.index.Term;import org.apache.commons.collections.iterators.EmptyIterator;import javax.jcr.RepositoryException;import java.io.IOException;import java.io.File;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Arrays;import java.util.Set;import java.util.HashSet;import java.util.HashMap;import java.util.Map;import java.util.Collection;/** * A <code>MultiIndex</code> consists of a {@link VolatileIndex} and multiple * {@link PersistentIndex}es. The goal is to keep most parts of the index open * with index readers and write new index data to the volatile index. When * the volatile index reaches a certain size (see {@link SearchIndex#setMinMergeDocs(int)}) * a new persistent index is created with the index data from the volatile index, * the same happens when the volatile index has been idle for some time (see * {@link SearchIndex#setVolatileIdleTime(int)}). * The new persistent index is then added to the list of already existing * persistent indexes. Further operations on the new persistent index will * however only require an <code>IndexReader</code> which serves for queries * but also for delete operations on the index. * <p/> * The persistent indexes are merged from time to time. The merge behaviour * is configurable using the methods: {@link SearchIndex#setMaxMergeDocs(int)}, * {@link SearchIndex#setMergeFactor(int)} and {@link SearchIndex#setMinMergeDocs(int)}. * For detailed description of the configuration parameters see also the lucene * <code>IndexWriter</code> class. * <p/> * This class is thread-safe. * <p/> * Note on implementation: Multiple modifying threads are synchronized on a * <code>MultiIndex</code> instance itself. Sychronization between a modifying * thread and reader threads is done using {@link #updateMonitor} and * {@link #updateInProgress}. */public class MultiIndex {    /**     * The logger instance for this class     */    private static final Logger log = LoggerFactory.getLogger(MultiIndex.class);    /**     * Default name of the redo log file     */    private static final String REDO_LOG = "redo.log";    /**     * Name of the file that contains the indexing queue log.     */    private static final String INDEXING_QUEUE_FILE = "indexing_queue.log";    /**     * Names of active persistent index directories.     */    private final IndexInfos indexNames = new IndexInfos("indexes");    /**     * Names of index directories that can be deleted.     */    private final IndexInfos deletable = new IndexInfos("deletable");    /**     * List of open persistent indexes. This list may also contain an open     * PersistentIndex owned by the IndexMerger daemon. Such an index is not     * registered with indexNames and <b>must not</b> be used in regular index     * operations (delete node, etc.)!     */    private final List indexes = new ArrayList();    /**     * The internal namespace mappings of the query manager.     */    private final NamespaceMappings nsMappings;    /**     * The base filesystem to store the index.     */    private final File indexDir;    /**     * The query handler     */    private final SearchIndex handler;    /**     * The volatile index.     */    private VolatileIndex volatileIndex;    /**     * Flag indicating whether an update operation is in progress.     */    private boolean updateInProgress = false;    /**     * If not <code>null</code> points to a valid <code>IndexReader</code> that     * reads from all indexes, including volatile and persistent indexes.     */    private CachingMultiReader multiReader;    /**     * Shared document number cache across all persistent indexes.     */    private final DocNumberCache cache;    /**     * Monitor to use to synchronize access to {@link #multiReader} and     * {@link #updateInProgress}.     */    private final Object updateMonitor = new Object();    /**     * <code>true</code> if the redo log contained entries on startup.     */    private boolean redoLogApplied = false;    /**     * The time this index was last flushed or a transaction was committed.     */    private long lastFlushTime;    /**     * The <code>IndexMerger</code> for this <code>MultiIndex</code>.     */    private final IndexMerger merger;    /**     * Timer to schedule flushes of this index after some idle time.     */    private static final Timer FLUSH_TIMER = new Timer(true);    /**     * Task that is periodically called by {@link #FLUSH_TIMER} and checks     * if index should be flushed.     */    private final Timer.Task flushTask;    /**     * The RedoLog of this <code>MultiIndex</code>.     */    private final RedoLog redoLog;    /**     * The indexing queue with pending text extraction jobs.     */    private IndexingQueue indexingQueue;    /**     * Set&lt;NodeId> of uuids that should not be indexed.     */    private final Set excludedIDs;    /**     * The next transaction id.     */    private long nextTransactionId = 0;    /**     * The current transaction id.     */    private long currentTransactionId = -1;    /**     * Flag indicating whether re-indexing is running.     */    private boolean reindexing = false;    /**     * Creates a new MultiIndex.     *     * @param indexDir the base file system     * @param handler the search handler     * @param stateMgr shared item state manager     * @param rootId id of the root node     * @param excludedIDs   Set&lt;NodeId> that contains uuids that should not     *                      be indexed nor further traversed.     * @param mapping the namespace mapping to use     * @throws IOException if an error occurs     */    MultiIndex(File indexDir,               SearchIndex handler,               ItemStateManager stateMgr,               NodeId rootId,               Set excludedIDs,               NamespaceMappings mapping) throws IOException {        this.indexDir = indexDir;        this.handler = handler;        this.cache = new DocNumberCache(handler.getCacheSize());        this.redoLog = new RedoLog(new File(indexDir, REDO_LOG));        this.excludedIDs = new HashSet(excludedIDs);        this.nsMappings = mapping;        if (indexNames.exists(indexDir)) {            indexNames.read(indexDir);        }        if (deletable.exists(indexDir)) {            deletable.read(indexDir);        }        // try to remove deletable files if there are any        attemptDelete();        // initialize IndexMerger        merger = new IndexMerger(this);        merger.setMaxMergeDocs(handler.getMaxMergeDocs());        merger.setMergeFactor(handler.getMergeFactor());        merger.setMinMergeDocs(handler.getMinMergeDocs());        IndexingQueueStore store;        try {            LocalFileSystem fs = new LocalFileSystem();            fs.setRoot(indexDir);            fs.init();            store = new IndexingQueueStore(fs, INDEXING_QUEUE_FILE);        } catch (FileSystemException e) {            IOException ex = new IOException();            ex.initCause(e);            throw ex;        }        // initialize indexing queue        this.indexingQueue = new IndexingQueue(store, this);        try {            // open persistent indexes            for (int i = 0; i < indexNames.size(); i++) {                File sub = new File(indexDir, indexNames.getName(i));                // only open if it still exists                // it is possible that indexNames still contains a name for                // an index that has been deleted, but indexNames has not been                // written to disk.                if (!sub.exists()) {                    log.debug("index does not exist anymore: " + sub.getAbsolutePath());                    // move on to next index                    continue;                }                PersistentIndex index = new PersistentIndex(                        indexNames.getName(i), sub, false,                        handler.getTextAnalyzer(), cache, indexingQueue);                index.setMaxMergeDocs(handler.getMaxMergeDocs());                index.setMergeFactor(handler.getMergeFactor());                index.setMinMergeDocs(handler.getMinMergeDocs());                index.setMaxFieldLength(handler.getMaxFieldLength());                index.setUseCompoundFile(handler.getUseCompoundFile());                indexes.add(index);                merger.indexAdded(index.getName(), index.getNumDocuments());            }            // init volatile index            resetVolatileIndex();            redoLogApplied = redoLog.hasEntries();            // run recovery            Recovery.run(this, redoLog);            // now that we are ready, start index merger            merger.start();            if (redoLogApplied) {                // wait for the index merge to finish pending jobs                try {                    merger.waitUntilIdle();                } catch (InterruptedException e) {                    // move on                }                flush();            }            // do an initial index if there are no indexes at all            if (indexNames.size() == 0) {                reindexing = true;                // traverse and index workspace                executeAndLog(new Start(Action.INTERNAL_TRANSACTION));                NodeState rootState = (NodeState) stateMgr.getItemState(rootId);                createIndex(rootState, stateMgr);                executeAndLog(new Commit(getTransactionId()));                reindexing = false;            }        } catch (ItemStateException e) {            throw new IOException("Error indexing root node: " + e.getMessage());        } catch (RepositoryException e) {            throw new IOException("Error indexing root node: " + e.getMessage());        }        lastFlushTime = System.currentTimeMillis();        flushTask = new Timer.Task() {            public void run() {                // check if there are any indexing jobs finished                checkIndexingQueue();                // check if volatile index should be flushed                checkFlush();            }        };        FLUSH_TIMER.schedule(flushTask, 0, 1000);    }    /**     * Atomically updates the index by removing some documents and adding     * others.     *     * @param remove Iterator of <code>UUID</code>s that identify documents to     *               remove     * @param add    Iterator of <code>Document</code>s to add. Calls to     *               <code>next()</code> on this iterator may return     *               <code>null</code>, to indicate that a node could not be     *               indexed successfully.     */    synchronized void update(Iterator remove, Iterator add) throws IOException {        synchronized (updateMonitor) {            updateInProgress = true;        }        try {            long transactionId = nextTransactionId++;            executeAndLog(new Start(transactionId));            boolean flush = false;            while (remove.hasNext()) {                executeAndLog(new DeleteNode(transactionId, (UUID) remove.next()));            }            while (add.hasNext()) {                Document doc = (Document) add.next();                if (doc != null) {                    executeAndLog(new AddNode(transactionId, doc));                    // commit volatile index if needed                    flush |= checkVolatileCommit();                }            }            executeAndLog(new Commit(transactionId));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?