Package org.hibernate.search.backend

Source Code of org.hibernate.search.backend.Workspace

//$Id: Workspace.java 15567 2008-11-16 15:06:50Z sannegrinovero $
package org.hibernate.search.backend;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.slf4j.Logger;

import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.search.SearchException;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.optimization.OptimizerStrategy;
import org.hibernate.search.util.LoggerFactory;

/**
* Lucene workspace for a DirectoryProvider.<p/>
* <ul>
* <li>Before using {@link #getIndexWriter} or {@link #getIndexReader} the lock must be acquired,
* and resources must be closed before releasing the lock.</li>
* <li>One cannot get an IndexWriter when an IndexReader has been acquired and not closed, and vice-versa.</li>
* <li>The recommended approach is to execute all the modifications on the <code>IndexReader</code>, and after that on
* the <code>IndexWriter</code></li>.
* </ul>
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @author Sanne Grinovero
*/
//TODO renaming to "DirectoryWorkspace" would be nice.
public class Workspace {

  private static final Logger log = LoggerFactory.make();
  private static final Analyzer SIMPLE_ANALYZER = new SimpleAnalyzer();
 
  // invariant state:

  private final SearchFactoryImplementor searchFactoryImplementor;
  private final DirectoryProvider directoryProvider;
  private final OptimizerStrategy optimizerStrategy;
  private final ReentrantLock lock;
  private final Set<Class<?>> entitiesInDirectory;
  private final LuceneIndexingParameters indexingParams;

  // variable state:
 
  /**
   * Current open IndexReader, or null when closed. Guarded by synchronization.
   */
  private IndexReader reader;
 
  /**
   * Current open IndexWriter, or null when closed. Guarded by synchronization.
   */
  private IndexWriter writer;
 
  /**
   * Keeps a count of modification operations done on the index.
   */
  private final AtomicLong operations = new AtomicLong( 0L );
 
  public Workspace(SearchFactoryImplementor searchFactoryImplementor, DirectoryProvider<?> provider) {
    this.searchFactoryImplementor = searchFactoryImplementor;
    this.directoryProvider = provider;
    this.optimizerStrategy = searchFactoryImplementor.getOptimizerStrategy( directoryProvider );
    this.entitiesInDirectory = searchFactoryImplementor.getClassesInDirectoryProvider( provider );
    this.indexingParams = searchFactoryImplementor.getIndexingParameters( directoryProvider );
    this.lock = searchFactoryImplementor.getDirectoryProviderLock( provider );
  }

  public <T> DocumentBuilder<T> getDocumentBuilder(Class<T> entity) {
    return searchFactoryImplementor.getDocumentBuilder( entity );
  }

  /**
   * If optimization has not been forced give a change to configured OptimizerStrategy
   * to optimize the index.
   * @throws AssertionFailure if the lock is not owned or if an IndexReader is open.
   */
  public void optimizerPhase() {
    assertOwnLock();
    // used getAndSet(0) because Workspace is going to be reused by next transaction.
    optimizerStrategy.addTransaction( operations.getAndSet( 0L ) );
    optimizerStrategy.optimize( this );
  }
 
  /**
   * Used by OptimizeLuceneWork after index optimization to flag that
   * optimization has been forced.
   * @see OptimizeLuceneWork
   * @see SearchFactory#optimize()
   * @see SearchFactory#optimize(Class)
   */
  public void optimize() {
    assertOwnLock(); // the DP is not affected, but needs to ensure the optimizerStrategy is accesses in threadsafe way
    optimizerStrategy.optimizationForced();
  }

  /**
   * Gets an IndexReader to alter the index, opening one if needed.
   * The caller needs to own the lock relevant to this DirectoryProvider.
   * @throws AssertionFailure if an IndexWriter is open or if the lock is not owned.
   * @return a new IndexReader or one already open.
   * @see #lock()
   */
  public synchronized IndexReader getIndexReader() {
    assertOwnLock();
    // one cannot access a reader for update while a writer is in use
    if ( writer != null )
      throw new AssertionFailure( "Tries to read for update an index while a writer is in use." );
    if ( reader != null )
      return reader;
    Directory directory = directoryProvider.getDirectory();
    try {
      reader = IndexReader.open( directory, false );
      log.trace( "IndexReader opened" );
    }
    catch ( IOException e ) {
      reader = null;
      throw new SearchException( "Unable to open IndexReader on directory " + directory, e );
    }
    return reader;
  }

  /**
   * Closes a previously opened IndexReader.
   * @throws SearchException on IOException during Lucene close operation.
   * @throws AssertionFailure if the lock is not owned or if there is no IndexReader to close.
   * @see #getIndexReader()
   */
  public synchronized void closeIndexReader() {
    assertOwnLock();
    IndexReader toClose = reader;
    reader = null;
    if ( toClose != null ) {
      try {
        toClose.close();
        log.trace( "IndexReader closed" );
      }
      catch ( IOException e ) {
        throw new SearchException( "Exception while closing IndexReader", e );
      }
    }
    else {
      throw new AssertionFailure( "No IndexReader open to close." );
    }
  }
 
  /**
   * Gets the IndexWriter, opening one if needed.
   * @param batchmode when true the indexWriter settings for batch mode will be applied.
   * Ignored if IndexWriter is open already.
   * @throws AssertionFailure if an IndexReader is open or the lock is not owned.
   * @throws SearchException on a IOException during index opening.
   * @return a new IndexWriter or one already open.
   */
  public synchronized IndexWriter getIndexWriter(boolean batchmode) {
    assertOwnLock();
    // one has to close a reader for update before a writer is accessed
    if ( reader != null )
      throw new AssertionFailure( "Tries to open an IndexWriter while an IndexReader is open in update mode." );
    if ( writer != null )
      return writer;
    try {
      // don't care about the Analyzer as it will be selected during usage of IndexWriter.
      IndexWriter.MaxFieldLength fieldLength = new IndexWriter.MaxFieldLength( IndexWriter.DEFAULT_MAX_FIELD_LENGTH );
      writer = new IndexWriter( directoryProvider.getDirectory(), SIMPLE_ANALYZER, false, fieldLength ); // has been created at init time
      indexingParams.applyToWriter( writer, batchmode );
      log.trace( "IndexWriter opened" );
    }
    catch ( IOException e ) {
      writer = null;
      throw new SearchException( "Unable to open IndexWriter", e );
    }
    return writer;
  }

  /**
   * Commits changes to a previously opened index writer.
   *
   * @throws SearchException on IOException during Lucene close operation.
   * @throws AssertionFailure if there is no IndexWriter to close, or if the lock is not owned.
   */
  public synchronized void commitIndexWriter() {
    assertOwnLock();
    if ( writer != null ) {
      try {
        writer.commit();
        log.trace( "Index changes commited." );
      }
      catch ( IOException e ) {
        throw new SearchException( "Exception while commiting index changes", e );
      }
    }
    else {
      throw new AssertionFailure( "No open IndexWriter to commit changes." );
    }
  }



  /**
   * Closes a previously opened IndexWriter.
   * @throws SearchException on IOException during Lucene close operation.
   * @throws AssertionFailure if there is no IndexWriter to close, or if the lock is not owned.
   */
  public synchronized void closeIndexWriter() {
    assertOwnLock();
    IndexWriter toClose = writer;
    writer = null;
    if ( toClose != null ) {
      try {
        toClose.close();
        log.trace( "IndexWriter closed" );
      }
      catch ( IOException e ) {
        throw new SearchException( "Exception while closing IndexWriter", e );
      }
    }
    else {
      throw new AssertionFailure( "No open IndexWriter to close" );
    }
  }

  /**
   * Increment the counter of modification operations done on the index.
   * Used (currently only) by the OptimizerStrategy.
   * @param modCount the increment to add to the counter.
   */
  public void incrementModificationCounter(int modCount) {
    operations.addAndGet( modCount );
  }

  /**
   * @return The unmodifiable set of entity types being indexed
   * in the underlying Lucene Directory backing this Workspace.
   */
  public Set<Class<?>> getEntitiesInDirectory() {
    return entitiesInDirectory;
  }
 
  /**
   * Acquires a lock on the DirectoryProvider backing this Workspace;
   * this is required to use getIndexWriter(boolean), closeIndexWriter(),
   * getIndexReader(), closeIndexReader().
   * @see #getIndexWriter(boolean)
   * @see #closeIndexWriter()
   * @see #getIndexReader()
   * @see #closeIndexReader()
   */
  public void lock() {
    lock.lock();
  }

  /**
   * Releases the lock obtained by calling lock()
   * @throws AssertionFailure when unlocking without having closed IndexWriter or IndexReader.
   * @see #lock()
   */
  public synchronized void unlock() {
    try {
      if ( this.reader != null ) {
        throw new AssertionFailure( "Unlocking Workspace without having closed the IndexReader" );
      }
      if ( this.writer != null ) {
        throw new AssertionFailure( "Unlocking Workspace without having closed the IndexWriter" );
      }
    }
    finally {
      lock.unlock();
    }
  }

  private void assertOwnLock() {
    if ( ! lock.isHeldByCurrentThread() )
      throw new AssertionFailure( "Not owning DirectoryProvider Lock" );
  }

}
TOP

Related Classes of org.hibernate.search.backend.Workspace

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.