/* * Copyright (C) 2009 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.index; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.index.MultiReader; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.util.Version; import org.xcmis.search.Startable; import org.xcmis.search.config.IndexConfiguration; import org.xcmis.search.config.IndexConfigurationException; import org.xcmis.search.content.IndexModificationException; import org.xcmis.search.lucene.index.merge.IndexAggregator; import org.xcmis.spi.utils.Logger; /** * Created by The eXo Platform SAS. * * @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a> * @version $Id: LocalStorageIndexDataManager.java 2 2010-02-04 17:21:49Z andrew00x $ */ public class LocalStorageIndexDataManager implements LuceneIndexDataManager, IndexAggregator, Startable { /** * */ private List<PersistedIndex> chains; /** * */ private final PersistentIndexDataKeeperFactory indexFactory; /** * Class logger. */ private static final Logger LOG = Logger.getLogger(LocalStorageIndexDataManager.class); private final IndexConfiguration indexConfuguration; /** * @throws IndexConfigurationException * @throws IndexException * @throws IndexConfigurationException */ public LocalStorageIndexDataManager(final IndexConfiguration indexConfuguration) throws IndexException, IndexConfigurationException { super(); this.indexConfuguration = indexConfuguration; indexFactory = new PersistentIndexDataKeeperFactory(indexConfuguration); } public IndexTransactionModificationReport aggregate(final Collection<LuceneIndexDataManager> indexes) throws IndexException, IndexTransactionException { if (chains.size() == 0) { chains.add((PersistedIndex)indexFactory.merge(indexes)); } else { IndexWriter writer = null; try { final PersistedIndex index = chains.get(0); writer = new IndexWriter(index.getDirectory(), new StandardAnalyzer(Version.LUCENE_35), MaxFieldLength.UNLIMITED); final List<Directory> dirs = new ArrayList<Directory>(); for (final LuceneIndexDataManager luceneIndexDataManager : indexes) { // TODO remove get reader luceneIndexDataManager.getIndexReader(); dirs.add(luceneIndexDataManager.getDirectory()); } final Directory[] dirsToMerge = new Directory[dirs.size()]; writer.addIndexesNoOptimize(dirs.toArray(dirsToMerge)); writer.optimize(); } catch (final CorruptIndexException e) { throw new IndexException(e.getLocalizedMessage(), e); } catch (final LockObtainFailedException e) { throw new IndexException(e.getLocalizedMessage(), e); } catch (final IOException e) { throw new IndexException(e.getLocalizedMessage(), e); } finally { if (writer != null) { try { writer.close(); } catch (CorruptIndexException e) { throw new IndexException(e.getLocalizedMessage(), e); } catch (IOException e) { throw new IndexException(e.getLocalizedMessage(), e); } } } } return null; } public Directory getDirectory() throws IndexException { if (chains.size() != 0) { return chains.get(0).getDirectory(); } return null; } public long getDirectorySize(final boolean includeInherited) { return 0; } public Document getDocument(final String uuid) throws IndexException { Document doc = null; synchronized (chains) { for (int i = 0; i < chains.size(); i++) { doc = chains.get(i).getDocument(uuid); if (doc != null) { break; } } } return doc; } public synchronized long getDocumentCount() { long result = 0; for (final PersistedIndex index : chains) { result += index.getDocumentCount(); } return result; } public IndexReader getIndexReader() throws IndexException { IndexReader result = null; if (chains.size() > 0) { synchronized (chains) { if (chains.size() > 0) { final List<IndexReader> readers = new ArrayList<IndexReader>(chains.size()); final Iterator<PersistedIndex> it = chains.iterator(); while (it.hasNext()) { final LuceneIndexDataManager chain = it.next(); final IndexReader indexReader = chain.getIndexReader(); if (indexReader != null) { readers.add(indexReader); } } if (result != null) { readers.add(result); } if (readers.size() > 1) { final IndexReader[] indexReaders = new IndexReader[readers.size()]; result = new MultiReader(readers.toArray(indexReaders)); } else if (readers.size() == 1) { result = readers.get(0); } else { throw new RuntimeException("No readers found"); } } } if (result == null) { throw new RuntimeException("No readers found"); } } return result; } public long getLastModifedTime() { return 0; } public boolean isStarted() { return false; } public boolean isStoped() { return false; } public IndexTransactionModificationReport save(IndexTransaction<Document> changes) throws IndexException, IndexTransactionException { // notify all chains about changes synchronized (chains) { if (changes.hasModifacationsDocuments()) { // reverse order changes = processModifed(changes); if (changes.getRemovedDocuments().size() > 0) { throw new IndexModificationException("Unable to remove item's with id's " + changes.getRemovedDocuments() + " from index"); } } // take care about added modification processAdded(changes); } return null; } /** * {@inheritDoc} */ public void start() { try { chains = indexFactory.init(); } catch (final IndexException e) { throw new RuntimeException(e.getLocalizedMessage(), e); } } /** * {@inheritDoc} */ public void stop() { for (final PersistedIndex index : chains) { try { index.getDirectory().close(); } catch (final IndexException e) { LOG.error(e.getMessage(), e); } catch (final IOException e) { LOG.error(e.getMessage(), e); } } } /** * Process add * * @param changes * @throws IndexException * @throws IndexTransactionException */ private void processAdded(final IndexTransaction<Document> changes) throws IndexException, IndexTransactionException { if (changes.getAddedDocuments().size() > 0) { synchronized (chains) { if (chains.size() == 0) { final LuceneIndexDataManager indexDataKeeper = indexFactory.createNewIndexDataKeeper(changes); indexDataKeeper.start(); chains.add((PersistedIndex)indexDataKeeper); } else { final LuceneIndexDataManager indexDataKeeper = chains.get(0); indexDataKeeper.save(changes); } } } } /** * Process remove and update * * @param changes * @return * @throws IndexException * @throws IndexTransactionException */ private IndexTransaction<Document> processModifed(IndexTransaction<Document> changes) throws IndexException, IndexTransactionException { synchronized (chains) { for (final Iterator<PersistedIndex> it = chains.iterator(); it.hasNext();) { final LuceneIndexDataManager chain = it.next(); final IndexTransactionModificationReport report = chain.save(changes); if (report.isModifed()) { changes = changes.apply(report); if (chain.getDocumentCount() == 0) { indexFactory.dispose(chain); it.remove(); } } if (!changes.hasModifacationsDocuments()) { break; } } } return changes; } }