/* * Copyright (C) 2010 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xcmis.search.lucene; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.xcmis.search.config.IndexConfiguration; import org.xcmis.search.content.ContentEntry; import org.xcmis.search.content.command.read.GetChildEntriesCommand; import org.xcmis.search.content.command.read.GetContentEntryCommand; import org.xcmis.search.content.command.read.GetUnfiledEntriesCommand; import org.xcmis.search.lucene.index.IndexException; import org.xcmis.search.lucene.index.IndexTransactionException; import org.xcmis.search.lucene.index.LuceneIndexTransaction; import org.xcmis.search.lucene.index.LuceneIndexer; import org.xcmis.spi.utils.Logger; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; /** * Tools for index recovering. */ public class IndexRecoveryTool { /** * Class logger. */ private static final Logger LOG = Logger.getLogger(IndexRecoveryTool.class); /** * Max documents count in buffer. */ public static final int BUFFER_MAX_SIZE = 1000; /** * Convert {@link ContentEntry} to {@link Document}. */ private final LuceneIndexer nodeIndexer; private final LuceneQueryableIndexStorage indexStorage; /** * Configuration of index. */ private final IndexConfiguration indexConfiguration; /** * @param indexStorage * @param nodeIndexer * @param indexConfiguration */ public IndexRecoveryTool(LuceneQueryableIndexStorage indexStorage, LuceneIndexer nodeIndexer, IndexConfiguration indexConfiguration) { super(); this.indexStorage = indexStorage; this.nodeIndexer = nodeIndexer; this.indexConfiguration = indexConfiguration; } /** * Refresh the index of documents returned by 'uuids' iterator. * @param uuids * @throws IndexException */ public void recover(Iterator<String> uuids) throws IndexException { IndexReader reader = indexStorage.getIndexReader(); try { final HashMap<String, Document> addedDocuments = new HashMap<String, Document>(); final HashSet<String> removedDocuments = new HashSet<String>(); while (uuids.hasNext()) { String nodeUuid = uuids.next(); GetContentEntryCommand getCommand = new GetContentEntryCommand(nodeUuid); final ContentEntry contentEntry = (ContentEntry)indexStorage.invokeNextInterceptor(null, getCommand); if (contentEntry == null) { if (indexStorage.getDocument(nodeUuid, reader) != null) { // item exist in index storage but doesn't exist in // persistence storage removedDocuments.add(nodeUuid); } } else { Document doc = nodeIndexer.createDocument(contentEntry); if (indexStorage.getDocument(nodeUuid, reader) != null) { // out dated content addedDocuments.put(nodeUuid, doc); removedDocuments.add(nodeUuid); } else { // content desn't exist addedDocuments.put(nodeUuid, doc); } } //flash if changes more when BUFFER_MAX_SIZE if (checkFlush(addedDocuments, removedDocuments)) { flash(addedDocuments, removedDocuments); } } //if some changes left if (addedDocuments.size() + removedDocuments.size() >= 0) { indexStorage.save(new LuceneIndexTransaction(addedDocuments, removedDocuments)); } } catch (Throwable e) { throw new IndexException(e.getLocalizedMessage(), e); } } /** * Recover all content. * @throws IndexException */ public void recoverAll() throws IndexException { Map<String, Document> documentBuffer = new HashMap<String, Document>(); final HashSet<String> removedDocuments = new HashSet<String>(); try { GetContentEntryCommand getCommand = new GetContentEntryCommand(indexConfiguration.getRootUuid()); final ContentEntry rootEntry = (ContentEntry)indexStorage.invokeNextInterceptor(null, getCommand); if (rootEntry != null) { restoreBranch(rootEntry, documentBuffer, removedDocuments); } else { LOG.warn("Root element with id " + indexConfiguration.getRootUuid() + " not found "); } if (documentBuffer.size() > 0) { flash(documentBuffer, removedDocuments); } //recover unfiled documents. GetUnfiledEntriesCommand getUnfiledEntriesCommand = new GetUnfiledEntriesCommand(); Iterator<String> uuids = (Iterator<String>)indexStorage.invokeNextInterceptor(null, getUnfiledEntriesCommand); if (uuids != null) { recover(uuids); } } catch (Throwable e) { throw new IndexException(e.getLocalizedMessage(), e); } } /** * * @param documentBuffer * @return true if we need to flush buffer. */ private boolean checkFlush(Map<String, Document> documentBuffer, HashSet<String> removedDocuments) { return documentBuffer.size() + removedDocuments.size() >= BUFFER_MAX_SIZE; } /** * Flash changes to the index and cleans the lists. * @param documentBuffer * @param removedDocuments * @throws IndexTransactionException * @throws IndexException */ private void flash(Map<String, Document> documentBuffer, HashSet<String> removedDocuments) throws IndexTransactionException, IndexException { indexStorage.save(new LuceneIndexTransaction(new HashMap<String, Document>(documentBuffer), new HashSet<String>( removedDocuments))); documentBuffer.clear(); removedDocuments.clear(); } /** * Restore content of branch starting from branchUuid. * * @param branchUuid * - Uuid of root element of branch. * @param documentBuffer * @param removedDocuments * @throws Throwable */ private void restoreBranch(ContentEntry branchRoot, Map<String, Document> documentBuffer, HashSet<String> removedDocuments) throws Throwable { // add root. documentBuffer.put(branchRoot.getIdentifier(), nodeIndexer.createDocument(branchRoot)); if (checkFlush(documentBuffer, removedDocuments)) { flash(documentBuffer, removedDocuments); } // add childs GetChildEntriesCommand getChildCommand = new GetChildEntriesCommand(branchRoot.getIdentifier()); Collection<ContentEntry> childEntries = (Collection<ContentEntry>)indexStorage.invokeNextInterceptor(null, getChildCommand); if (childEntries != null) { for (ContentEntry contentEntry : childEntries) { restoreBranch(contentEntry, documentBuffer, removedDocuments); } } else { LOG.warn("Child elements for element with id " + branchRoot.getIdentifier() + " is not found "); } } }