org.springmodules.lucene.index.core.DefaultLuceneIndexTemplate.java Source code

Java tutorial

Introduction

Here is the source code for org.springmodules.lucene.index.core.DefaultLuceneIndexTemplate.java

Source

/*
 * Copyright 2002-2005 the original author or authors.
 *
 * 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.springmodules.lucene.index.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.springmodules.lucene.index.LuceneIndexAccessException;
import org.springmodules.lucene.index.core.DocumentCreator;
import org.springmodules.lucene.index.core.DocumentIdentifier;
import org.springmodules.lucene.index.core.DocumentModifier;
import org.springmodules.lucene.index.core.DocumentsCreator;
import org.springmodules.lucene.index.core.DocumentsIdentifier;
import org.springmodules.lucene.index.core.DocumentsModifier;
import org.springmodules.lucene.index.core.InputStreamDocumentCreator;
import org.springmodules.lucene.index.core.LuceneIndexTemplate;
import org.springmodules.lucene.index.core.ReaderCallback;
import org.springmodules.lucene.index.core.WriterCallback;
import org.springmodules.lucene.index.factory.IndexFactory;
import org.springmodules.lucene.index.factory.IndexReaderFactoryUtils;
import org.springmodules.lucene.index.factory.IndexWriterFactoryUtils;
import org.springmodules.lucene.search.factory.SearcherFactoryUtils;
import org.springmodules.lucene.util.IOUtils;

/**
 * <b>This is the central class in the lucene indexing core package.</b>
 * It simplifies the use of lucene to index documents or datas using
 * index reader and writer. It helps to avoid common errors and to
 * manage these resource in a flexible manner.
 * It executes core Lucene workflow, leaving application code to focus on
 * the way to create Lucene documents and make some operations on the
 * index.
 *
 * <p>This class is based on the IndexFactory abstraction which is a
 * factory to create IndexReader and IndexWriter for the configured
 * Directory. So the template doesn't need to always hold resources and
 * this avoids some locking problems on the index. You can too apply
 * different strategies for managing index resources.
 *
 * <p>Can be used within a service implementation via direct instantiation
 * with a IndexFactory reference, or get prepared in an application context
 * and given to services as bean reference. Note: The IndexFactory should
 * always be configured as a bean in the application context, in the first case
 * given to the service directly, in the second case to the prepared template.
 * 
 * <p>You must be aware that the use of some methods (like undeleteDocuments,
 * isDeleted, hasDeletions, flush) have sense only if you share the Lucene
 * underlying resources across several template method calls. As a matter of
 * fact, when IndexReader and IndexWriter are closed every changes deferred
 * until the closing of these resources. Moreover some Lucene operations are
 * incompatible if you share resources across several calls. 
 * 
 * @author Brian McCallister
 * @author Thierry Templier
 * @see DocumentCreator
 * @see DocumentsCreator
 * @see org.springmodules.lucene.index.factory
 */
public class DefaultLuceneIndexTemplate implements LuceneIndexTemplate {

    private IndexFactory indexFactory;
    private Analyzer analyzer;

    /**
     * Construct a new LuceneIndexTemplate for bean usage.
     * Note: The IndexFactory has to be set before using the instance.
     * This constructor can be used to prepare a LuceneIndexTemplate via a BeanFactory,
     * typically setting the IndexFactory via setIndexFactory.
     * @see #setIndexFactory
     */
    public DefaultLuceneIndexTemplate() {
    }

    /**
     * Construct a new LuceneIndexTemplate, given an IndexFactory to obtain both
     * IndexReader and IndexWriter, and an Analyzer to be used unless an other
     * one is specified as method parameter.
     * @param indexFactory IndexFactory to obtain both IndexReader and IndexWriter
     * @param analyzer Lucene analyzer to extract tokens out of the text to index
     */
    public DefaultLuceneIndexTemplate(IndexFactory indexFactory, Analyzer analyzer) {
        setIndexFactory(indexFactory);
        setAnalyzer(analyzer);
        afterPropertiesSet();
    }

    /**
     * Check if the indexFactory is set. The analyzer could be not set.
     */
    public void afterPropertiesSet() {
        if (getIndexFactory() == null) {
            throw new IllegalArgumentException("indexFactory is required");
        }
    }

    /**
     * Set the IndexFactory to obtain both IndexReader and IndexWriter.
     */
    public void setIndexFactory(IndexFactory factory) {
        indexFactory = factory;
    }

    /**
     * Return the IndexFactory used by this template.
     */
    public IndexFactory getIndexFactory() {
        return indexFactory;
    }

    /**
     * Set the default Lucene Analyzer used to extract tokens out of the
     * text to index.
     */
    public void setAnalyzer(Analyzer analyzer) {
        this.analyzer = analyzer;
    }

    /**
     * Return the Lucene Analyzer used by this template.
     */
    public Analyzer getAnalyzer() {
        return analyzer;
    }

    //-------------------------------------------------------------------------
    // Methods dealing with document deletions
    //-------------------------------------------------------------------------

    public void deleteDocument(int internalDocumentId) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            reader.delete(internalDocumentId);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during deleting a document.", ex);
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public void deleteDocuments(Term term) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            reader.delete(term);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during deleting a document.", ex);
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public void undeleteDocuments() {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            reader.undeleteAll();
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during undeleting all documents.", ex);
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public boolean isDeleted(int internalDocumentId) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            return reader.isDeleted(internalDocumentId);
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public boolean hasDeletions() {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            return reader.hasDeletions();
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    //-------------------------------------------------------------------------
    // Methods dealing with index informations
    //-------------------------------------------------------------------------

    public int getMaxDoc() {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            return reader.maxDoc();
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public int getNumDocs() {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            return reader.numDocs();
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    //-------------------------------------------------------------------------
    // Methods dealing with document creations
    //-------------------------------------------------------------------------

    protected Document createDocument(DocumentCreator documentCreator) {
        try {
            return documentCreator.createDocument();
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Construction of the desired Document failed", ex);
        }
    }

    protected List createDocuments(DocumentsCreator documentsCreator) {
        try {
            return documentsCreator.createDocuments();
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Construction of the desired Document failed", ex);
        }
    }

    public void addDocument(Document document) {
        addDocument(document, null);
    }

    public void addDocument(Document document, Analyzer analyzer) {
        IndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(indexFactory);
        try {
            doAddDocument(writer, document, null);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during adding a document.", ex);
        } finally {
            IndexWriterFactoryUtils.releaseIndexWriter(indexFactory, writer);
        }
    }

    public void addDocument(DocumentCreator creator) {
        addDocument(createDocument(creator), null);
    }

    public void addDocument(DocumentCreator documentCreator, Analyzer analyzer) {
        addDocument(createDocument(documentCreator), analyzer);
    }

    public void addDocument(InputStreamDocumentCreator creator) {
        addDocument(creator, null);
    }

    public void addDocument(InputStreamDocumentCreator documentCreator, Analyzer analyzer) {
        InputStream inputStream = null;
        try {
            inputStream = documentCreator.createInputStream();
            addDocument(documentCreator.createDocumentFromInputStream(inputStream), analyzer);
        } catch (IOException ex) {
            //throw new LuceneInputStreamException("Error during adding a document.",ex);
            throw new RuntimeException("Error during adding a document.", ex);
        } finally {
            IOUtils.closeInputStream(inputStream);
        }
    }

    public void addDocuments(List documents) {
        addDocuments(documents, null);
    }

    public void addDocuments(List documents, Analyzer analyzer) {
        IndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(indexFactory);
        try {
            for (Iterator i = documents.iterator(); i.hasNext();) {
                Document document = (Document) i.next();
                doAddDocument(writer, document, analyzer);
            }
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during adding a document.", ex);
        } finally {
            IndexWriterFactoryUtils.releaseIndexWriter(indexFactory, writer);
        }
    }

    public void addDocuments(DocumentsCreator creator) {
        addDocuments(creator, null);
    }

    public void addDocuments(DocumentsCreator creator, Analyzer analyzer) {
        addDocuments(createDocuments(creator), analyzer);
    }

    private void doAddDocument(IndexWriter writer, Document document, Analyzer analyzer) throws IOException {
        if (document != null) {
            if (analyzer == null) {
                writer.addDocument(document);
            } else if (getAnalyzer() == null) {
                writer.addDocument(document);
            } else if (analyzer != null) {
                writer.addDocument(document, analyzer);
            } else if (getAnalyzer() != null) {
                writer.addDocument(document, getAnalyzer());
            } else {
                writer.addDocument(document);
            }
        } else {
            throw new LuceneIndexAccessException("The document created is null.");
        }
    }

    //-------------------------------------------------------------------------
    // Methods dealing with document updates
    //-------------------------------------------------------------------------

    private void checkHitsForUpdate(Hits hits) {
        if (hits.length() == 0) {
            throw new LuceneIndexAccessException("The identifier returns no document.");
        }
        if (hits.length() > 1) {
            throw new LuceneIndexAccessException("The identifier returns more than one document.");
        }
    }

    public void updateDocument(DocumentModifier documentModifier, DocumentIdentifier identifier) {
        updateDocument(documentModifier, identifier, null);
    }

    public void updateDocument(DocumentModifier documentModifier, DocumentIdentifier identifier,
            Analyzer analyzer) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        IndexSearcher searcher = new IndexSearcher(reader);
        Term identifierTerm = identifier.getIdentifier();
        Document updatedDocument = null;
        try {
            Hits hits = searcher.search(new TermQuery(identifierTerm));
            checkHitsForUpdate(hits);
            updatedDocument = documentModifier.updateDocument(hits.doc(0));
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during updating a document.", ex);
        } finally {
            SearcherFactoryUtils.releaseSearcher(searcher);
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }

        deleteDocuments(identifierTerm);
        addDocument(updatedDocument, analyzer);
    }

    public void updateDocuments(DocumentsModifier documentsModifier, DocumentsIdentifier identifier) {
        updateDocuments(documentsModifier, identifier, null);
    }

    public void updateDocuments(DocumentsModifier documentsModifier, DocumentsIdentifier identifier,
            Analyzer analyzer) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        IndexSearcher searcher = new IndexSearcher(reader);
        Term identifierTerm = identifier.getIdentifier();
        List updatedDocuments = null;
        try {
            Hits hits = searcher.search(new TermQuery(identifierTerm));
            updatedDocuments = documentsModifier.updateDocuments(hits);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during updating a document.", ex);
        } finally {
            SearcherFactoryUtils.releaseSearcher(searcher);
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }

        deleteDocuments(identifierTerm);
        addDocuments(updatedDocuments, analyzer);
    }

    //-------------------------------------------------------------------------
    // Methods dealing with index insertions
    //-------------------------------------------------------------------------

    public void addIndex(Directory directory) {
        addIndexes(new Directory[] { directory });
    }

    public void addIndexes(Directory[] directories) {
        IndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(indexFactory);
        try {
            writer.addIndexes(directories);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during adding indexes.", ex);
        } finally {
            IndexWriterFactoryUtils.releaseIndexWriter(indexFactory, writer);
        }
    }

    //-------------------------------------------------------------------------
    // Methods dealing with index optmization
    //-------------------------------------------------------------------------

    public void optimize() {
        IndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(indexFactory);
        try {
            writer.optimize();
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during optimize the index.", ex);
        } finally {
            IndexWriterFactoryUtils.releaseIndexWriter(indexFactory, writer);
        }
    }

    //-------------------------------------------------------------------------
    // Methods dealing with index reader and writer directly
    //-------------------------------------------------------------------------

    public Object read(ReaderCallback callback) {
        IndexReader reader = IndexReaderFactoryUtils.getIndexReader(indexFactory);
        try {
            return callback.doWithReader(reader);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during using the IndexReader.", ex);
        } finally {
            IndexReaderFactoryUtils.releaseIndexReader(indexFactory, reader);
        }
    }

    public Object write(WriterCallback callback) {
        IndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(indexFactory);
        try {
            return callback.doWithWriter(writer);
        } catch (IOException ex) {
            throw new LuceneIndexAccessException("Error during using the IndexWriter.", ex);
        } finally {
            IndexWriterFactoryUtils.releaseIndexWriter(indexFactory, writer);
        }
    }

}