org.weborganic.flint.SearcherManager.java Source code

Java tutorial

Introduction

Here is the source code for org.weborganic.flint.SearcherManager.java

Source

/*
 * This file is part of the Flint library.
 *
 * For licensing information please see the file license.txt included in the release.
 * A copy of this licence can also be found at
 *   http://www.opensource.org/licenses/artistic-license-2.0.php
 */
package org.weborganic.flint;

import java.io.IOException;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.AlreadyClosedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Manager for searches.
 *
 * <p>Usage:
 * <pre>
 * IndexSearcher searcher = searcherManager.get();
 * try {
 *    <do searching & rendering here>
 *  } finally {
 *    searcherManager.release(searcher);
 *  }
 * </pre>
 *
 * @author Jean-Baptiste Reure
 * @version 26 February 2010
 */
public class SearcherManager {

    /**
     * Logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SearcherManager.class);

    /**
     * The current searcher used to run searches on the index
     */
    private IndexSearcher currentSearcher;

    /**
     * Create a new SearcherManager using the given writer.
     *
     * @param writer the IndexWriter used to load the real-time reader.
     * @throws IOException If thrown while trying to get the reader.
     */
    public SearcherManager(IndexWriter writer) throws IOException {
        IndexReader reader = writer.getReader();
        // Lucene 3.1+: IndexReader.open(writer, true);
        this.currentSearcher = new IndexSearcher(reader);
    }

    /**
     * Create a new SearcherManager using the given writer.
     *
     * @param reader the IndexWriter used to load the real-time reader.
     * @throws IOException If thrown while trying to get the reader.
     */
    public SearcherManager(IndexReader reader) throws IOException {
        this.currentSearcher = new IndexSearcher(reader);
    }

    protected int getRefCount() {
        return this.currentSearcher.getIndexReader().getRefCount();
    }

    // ------------------ Index re-opening methods -----------------------------

    /**
     * flag used to set current state.
     */
    private boolean reopening = false;

    /**
     * Start re-open
     */
    private synchronized void startReopen() throws InterruptedException {
        LOGGER.debug("Starting open");
        while (this.reopening) {
            wait();
        }
        this.reopening = true;
    }

    /**
     * Conclude re-open.
     */
    private synchronized void doneReopen() {
        LOGGER.debug("Finishing open");
        this.reopening = false;
        notifyAll();
    }

    /**
     * Trigger a check to reopen the reader.
     *
     * @throws InterruptedException
     * @throws IOException
     */
    public void maybeReopen() throws InterruptedException, IOException {
        startReopen();
        try {
            final IndexSearcher searcher = get();
            try {
                if (!this.currentSearcher.getIndexReader().isCurrent()) {
                    // if not current, we need to re-open it
                    IndexReader newReader = this.currentSearcher.getIndexReader().reopen();
                    IndexSearcher newSearcher = new IndexSearcher(newReader);
                    swapSearcher(newSearcher);
                } else {
                    LOGGER.debug("Reader is still current so no need to re-open it");
                }
            } finally {
                release(searcher);
            }
        } finally {
            doneReopen();
        }
    }

    /**
     * Perform a swap between current searcher and given searcher.
     *
     * @param newSearcher
     * @throws IOException
     */
    private synchronized void swapSearcher(IndexSearcher newSearcher) throws IOException {
        LOGGER.debug("Swapping reader from {} to {}", this.currentSearcher.getIndexReader().hashCode(),
                newSearcher.getIndexReader().hashCode());
        release(this.currentSearcher);
        this.currentSearcher = newSearcher;
    }

    /**
     * Check if the reader provided is not current and not used anymore in which case it is closed.
     *
     * @param reader the reader to check
     *
     * @throws IOException if closing failed
     */
    private void closeIfDirty(IndexReader reader) throws IOException {
        // check if we should close an old one
        if (this.currentSearcher.getIndexReader() != reader) {
            if (reader.getRefCount() == 0) {
                LOGGER.debug("Closing reader {}", reader.hashCode());
                try {
                    reader.close();
                } catch (AlreadyClosedException e) {
                    // good then, no need to worry
                }
            } else {
                LOGGER.debug("Cannot close reader {} as there are still references ({})", reader.hashCode(),
                        reader.getRefCount());
            }
        }
    }

    /**
     * Close this searcher by closing the current searcher and its current reader.
     *
     * @throws IOException If closing this searcher failed
     */
    protected final void close() throws IOException {
        // close reader
        this.currentSearcher.getIndexReader().close();
        // then searcher
        this.currentSearcher.close();
    }

    /**
     * Return the current IndexSearcher. Important: call release() when finished with the searcher.
     *
     * @return the current IndexSearcher
     * @throws InterruptedException
     */
    protected synchronized IndexSearcher get() {
        this.currentSearcher.getIndexReader().incRef();
        LOGGER.debug("Getting reader {}", this.currentSearcher.getIndexReader().hashCode());
        return this.currentSearcher;
    }

    /**
     * Release the given searcher.
     *
     * @param searcher
     * @throws IOException
     */
    protected synchronized void release(IndexSearcher searcher) throws IOException {
        LOGGER.debug("Releasing reader {}", searcher.getIndexReader().hashCode());
        searcher.getIndexReader().decRef();
        // check if we should close an old one
        closeIfDirty(searcher.getIndexReader());
    }

    /**
     * Return the current IndexReader. Important: call releaseReader() when finished with the Index Reader.
     *
     * @return the current IndexReader
     *
     * @throws InterruptedException
     */
    protected synchronized IndexReader getReader() {
        LOGGER.debug("Getting reader {}", this.currentSearcher.getIndexReader().hashCode());
        this.currentSearcher.getIndexReader().incRef();
        return this.currentSearcher.getIndexReader();
    }

    /**
     * Release the given reader.
     *
     * @param reader the reader to release
     *
     * @throws IOException If thrown when attempting to close the reader, when reader is no longer in use.
     */
    protected synchronized void releaseReader(IndexReader reader) throws IOException {
        LOGGER.debug("Releasing reader {}", reader.hashCode());
        reader.decRef();
        // check if we should close an old one
        closeIfDirty(reader);
    }

}