nl.strohalm.cyclos.utils.lucene.IndexHandler.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.utils.lucene.IndexHandler.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.utils.lucene;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nl.strohalm.cyclos.dao.IndexOperationDAO;
import nl.strohalm.cyclos.entities.IndexOperation;
import nl.strohalm.cyclos.entities.IndexOperation.EntityType;
import nl.strohalm.cyclos.entities.IndexOperation.OperationType;
import nl.strohalm.cyclos.entities.IndexStatus;
import nl.strohalm.cyclos.entities.Indexable;
import nl.strohalm.cyclos.entities.exceptions.DaoException;
import nl.strohalm.cyclos.utils.ClassHelper;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * Handles configuration and operation of Lucene indexes
 * @author luis
 */
public class IndexHandler implements InitializingBean, DisposableBean {

    private static final Log LOG = LogFactory.getLog(IndexHandler.class);

    /**
     * Returns the root directory where indexes are stored
     */
    public static File resolveIndexRoot() {
        // Setup the Lucene index directory to WEB-INF/indexes directory
        final File bin = FileUtils.toFile(IndexHandler.class.getResource("/")); // WEB-INF/classes
        File root = bin.getParentFile(); // WEB-INF
        // When running on the standalone server, the bin is root/bin, not root/web/WEB-INF/classes
        if (!bin.getAbsolutePath().contains("WEB-INF") && new File(root, "web").exists()) {
            root = new File(root, "web/WEB-INF");
        }
        return new File(root, "indexes"); // WEB-INF/indexes
    }

    private File indexRoot;
    private IndexOperationDAO indexOperationDao;
    private Map<Class<? extends Indexable>, DocumentMapper> documentMappers;
    private Map<Class<? extends Indexable>, Directory> directories;

    @Override
    public void afterPropertiesSet() throws Exception {
        indexRoot = resolveIndexRoot();
        if (!indexRoot.exists()) {
            indexRoot.mkdirs();
        }
        if (indexRoot == null) {
            throw new IllegalStateException("No write access to indexes directory");
        }
        // Initialize the directories
        directories = new HashMap<Class<? extends Indexable>, Directory>();
        for (EntityType entityType : EntityType.values()) {
            Class<? extends Indexable> entityClass = entityType.getEntityClass();
            final File dir = getIndexDir(entityClass);
            FSDirectory directory = FSDirectory.open(dir);
            directories.put(entityClass, directory);
        }
    }

    @Override
    public void destroy() throws Exception {
        if (directories != null) {
            for (Map.Entry<Class<? extends Indexable>, Directory> entry : directories.entrySet()) {
                try {
                    entry.getValue().close();
                } catch (Exception e) {
                    LOG.warn("Error closing index directory for " + entry.getKey(), e);
                }
            }
            directories = null;
        }
    }

    /**
     * Returns the lucene directory for the given entity type
     */
    public Directory getDirectory(final Class<? extends Indexable> entityType) {
        return directories.get(entityType);
    }

    /**
     * Returns the {@link DocumentMapper} for the given entity type
     */
    public DocumentMapper getDocumentMapper(final Class<? extends Indexable> entityType) {
        return documentMappers.get(entityType);
    }

    /**
     * Returns the directory where the index is stored
     */
    public File getIndexDir(final Class<? extends Indexable> entityType) {
        return new File(indexRoot, ClassHelper.getClassName(entityType));
    }

    /**
     * Returns the root directory for all indexes
     */
    public File getIndexRoot() {
        return indexRoot;
    }

    /**
     * Returns the index status for the given entity type
     */
    public IndexStatus getIndexStatus(final Class<? extends Indexable> entityType) {
        IndexReader reader;
        try {
            reader = doOpenReader(entityType);
        } catch (final FileNotFoundException e) {
            return IndexStatus.MISSING;
        } catch (final IOException e) {
            return IndexStatus.CORRUPT;
        }
        try {
            // The isCurrent call will force the check for corrupted indexes
            reader.isCurrent();
            return IndexStatus.ACTIVE;
        } catch (final CorruptIndexException e) {
            return IndexStatus.CORRUPT;
        } catch (final Exception e) {
            LOG.warn("Error while retrieving the index status for " + entityType, e);
            throw new DaoException(e);
        } finally {
            try {
                reader.close();
            } catch (final IOException e) {
                // Silently ignore
            }
        }
    }

    /**
     * Adds the given entity to index, or updates it if already on index
     */
    public void index(final Class<? extends Indexable> entityType, final Long id) {
        createOperation(entityType, OperationType.ADD, id);
    }

    /**
     * Returns whether index dir exists
     */
    public boolean indexesExists() {
        return indexRoot.exists() && indexRoot.list().length > 0;
    }

    /**
     * Opens a new {@link IndexReader} for the given entity type
     */
    public IndexReader openReader(final Class<? extends Indexable> entityType) {
        try {
            return doOpenReader(entityType);
        } catch (final Exception e) {
            LOG.warn("Error while opening index for read on " + entityType, e);
            throw new DaoException(e);
        }
    }

    /**
     * Recreates the index for the given entity type
     */
    public void rebuild(final Class<? extends Indexable> entityType) {
        createOperation(entityType, OperationType.REBUILD);
    }

    /**
     * Recreates the index for the given entity type if it is corrupt
     */
    public void rebuildIfCorrupt(final Class<? extends Indexable> entityType) {
        createOperation(entityType, OperationType.REBUILD_IF_CORRUPT);
    }

    /**
     * Removes the given entities from index
     */
    public void remove(final Class<? extends Indexable> entityType, final List<Long> ids) {
        for (Long id : ids) {
            remove(entityType, id);
        }
    }

    /**
     * Removes the given entity from index
     */
    public void remove(final Class<? extends Indexable> entityType, final Long id) {
        createOperation(entityType, OperationType.REMOVE, id);
    }

    public void setDocumentMappers(final Map<Class<? extends Indexable>, DocumentMapper> documentMappers) {
        this.documentMappers = documentMappers;
    }

    public void setIndexOperationDao(final IndexOperationDAO indexOperationDao) {
        this.indexOperationDao = indexOperationDao;
    }

    private IndexOperation createOperation(final Class<? extends Indexable> entityType,
            final OperationType operationType) {
        return createOperation(entityType, operationType, null);
    }

    private IndexOperation createOperation(final Class<? extends Indexable> entityType,
            final OperationType operationType, final Long entityId) {
        IndexOperation operation = new IndexOperation();
        operation.setDate(Calendar.getInstance());
        operation.setEntityType(EntityType.from(entityType));
        operation.setOperationType(operationType);
        operation.setEntityId(entityId);
        return indexOperationDao.insert(operation);
    }

    @SuppressWarnings("deprecation")
    private IndexReader doOpenReader(final Class<? extends Indexable> entityType)
            throws CorruptIndexException, IOException {
        // TODO if we ever update to Lucene 4 (alpha for now) we shoudln't pass the true parameter, as readers will be always readonly. We won't
        // do it now because readonly readers perform better on high concurrency
        return IndexReader.open(getDirectory(entityType), true);
    }

}