Package org.hibernate.search.impl

Source Code of org.hibernate.search.impl.SearchFactoryImpl

//$Id: SearchFactoryImpl.java 13958 2007-08-29 16:26:07Z epbernard $
package org.hibernate.search.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
import java.lang.reflect.Method;
import java.beans.Introspector;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.search.Environment;
import org.hibernate.search.SearchException;
import org.hibernate.search.Version;
import org.hibernate.search.filter.FilterCachingStrategy;
import org.hibernate.search.filter.MRUFilterCachingStrategy;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.FullTextFilterDef;
import org.hibernate.search.annotations.Factory;
import org.hibernate.search.annotations.Key;
import org.hibernate.search.annotations.FullTextFilterDefs;
import org.hibernate.search.backend.BackendQueueProcessorFactory;
import org.hibernate.search.backend.LuceneIndexingParameters;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.OptimizeLuceneWork;
import org.hibernate.search.backend.Worker;
import org.hibernate.search.backend.WorkerFactory;
import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.engine.FilterDef;
import org.hibernate.search.reader.ReaderProvider;
import org.hibernate.search.reader.ReaderProviderFactory;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.DirectoryProviderFactory;
import org.hibernate.search.store.optimization.OptimizerStrategy;
import org.hibernate.util.ReflectHelper;

/**
* @author Emmanuel Bernard
*/
public class SearchFactoryImpl implements SearchFactoryImplementor {
  private static ThreadLocal<WeakHashMap<Configuration, SearchFactoryImpl>> contexts =
      new ThreadLocal<WeakHashMap<Configuration, SearchFactoryImpl>>();

  static {
    Version.touch();
  }

  private Map<Class, DocumentBuilder<Object>> documentBuilders = new HashMap<Class, DocumentBuilder<Object>>();
  //keep track of the index modifiers per DirectoryProvider since multiple entity can use the same directory provider
  private Map<DirectoryProvider, ReentrantLock> lockableDirectoryProviders =
      new HashMap<DirectoryProvider, ReentrantLock>();
  private Map<DirectoryProvider, OptimizerStrategy> dirProviderOptimizerStrategies =
      new HashMap<DirectoryProvider, OptimizerStrategy>();
  private Worker worker;
  private ReaderProvider readerProvider;
  private BackendQueueProcessorFactory backendQueueProcessorFactory;
  private Map<String, FilterDef> filterDefinitions = new HashMap<String, FilterDef>();
  private FilterCachingStrategy filterCachingStrategy;

  /**
   * Each directory provider (index) can have its own performance settings.
   */
  private Map<DirectoryProvider, LuceneIndexingParameters> dirProviderIndexingParams =
    new HashMap<DirectoryProvider, LuceneIndexingParameters>();


  public BackendQueueProcessorFactory getBackendQueueProcessorFactory() {
    return backendQueueProcessorFactory;
  }

  public void setBackendQueueProcessorFactory(BackendQueueProcessorFactory backendQueueProcessorFactory) {
    this.backendQueueProcessorFactory = backendQueueProcessorFactory;
  }

  @SuppressWarnings( "unchecked" )
  public SearchFactoryImpl(Configuration cfg) {
    //yuk
    ReflectionManager reflectionManager = getReflectionManager( cfg );

    Analyzer analyzer = initAnalyzer(cfg);
    initDocumentBuilders(cfg, reflectionManager, analyzer);

    Set<Class> indexedClasses = documentBuilders.keySet();
    for (DocumentBuilder builder : documentBuilders.values()) {
      builder.postInitialize( indexedClasses );
    }
    worker = WorkerFactory.createWorker( cfg, this );
    readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
    buildFilterCachingStrategy( cfg.getProperties() );
  }

  private void bindFilterDefs(XClass mappedXClass) {
    FullTextFilterDef defAnn = mappedXClass.getAnnotation( FullTextFilterDef.class );
    if ( defAnn != null ) {
      bindFilterDef( defAnn, mappedXClass );
    }
    FullTextFilterDefs defsAnn = mappedXClass.getAnnotation( FullTextFilterDefs.class );
    if (defsAnn != null) {
      for ( FullTextFilterDef def : defsAnn.value() ) {
        bindFilterDef( def, mappedXClass );
      }
    }
  }

  private void bindFilterDef(FullTextFilterDef defAnn, XClass mappedXClass) {
    if ( filterDefinitions.containsKey( defAnn.name() ) ) {
      throw new SearchException("Multiple definition of @FullTextFilterDef.name=" + defAnn.name() + ": "
          + mappedXClass.getName() );
    }
    FilterDef filterDef = new FilterDef();
    filterDef.setImpl( defAnn.impl() );
    filterDef.setCache( defAnn.cache() );
    try {
      filterDef.getImpl().newInstance();
    }
    catch (IllegalAccessException e) {
      throw new SearchException("Unable to create Filter class: " + filterDef.getImpl().getName(), e);
    }
    catch (InstantiationException e) {
      throw new SearchException("Unable to create Filter class: " + filterDef.getImpl().getName(), e);
    }
    for ( Method method : filterDef.getImpl().getMethods() ) {
      if ( method.isAnnotationPresent( Factory.class ) ) {
        if ( filterDef.getFactoryMethod() != null ) {
          throw new SearchException("Multiple @Factory methods found" + defAnn.name() + ": "
              + filterDef.getImpl().getName() + "." + method.getName() );
        }
        if ( !method.isAccessible() ) method.setAccessible( true );
        filterDef.setFactoryMethod( method );
      }
      if ( method.isAnnotationPresent( Key.class ) ) {
        if ( filterDef.getKeyMethod() != null ) {
          throw new SearchException("Multiple @Key methods found" + defAnn.name() + ": "
              + filterDef.getImpl().getName() + "." + method.getName() );
        }
        if ( !method.isAccessible() ) method.setAccessible( true );
        filterDef.setKeyMethod( method );
      }

      String name = method.getName();
      if ( name.startsWith( "set" ) && method.getParameterTypes().length == 1 ) {
        filterDef.addSetter( Introspector.decapitalize( name.substring( 3 ) ), method );
      }
    }
    filterDefinitions.put( defAnn.name(), filterDef );
  }

  //code doesn't have to be multithreaded because SF creation is not.
  //this is not a public API, should really only be used during the SessionFActory building
  //FIXME this is ugly, impl.staticmethod, fix that
  public static SearchFactoryImpl getSearchFactory(Configuration cfg) {
    WeakHashMap<Configuration, SearchFactoryImpl> contextMap = contexts.get();
    if ( contextMap == null ) {
      contextMap = new WeakHashMap<Configuration, SearchFactoryImpl>( 2 );
      contexts.set( contextMap );
    }
    SearchFactoryImpl searchFactory = contextMap.get( cfg );
    if ( searchFactory == null ) {
      searchFactory = new SearchFactoryImpl( cfg );

      contextMap.put( cfg, searchFactory );
    }
    return searchFactory;
  }


  public Map<Class, DocumentBuilder<Object>> getDocumentBuilders() {
    return documentBuilders;
  }

  public Map<DirectoryProvider, ReentrantLock> getLockableDirectoryProviders() {
    return lockableDirectoryProviders;
  }

  public Worker getWorker() {
    return worker;
  }

  public void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy optimizerStrategy) {
    dirProviderOptimizerStrategies.put( provider, optimizerStrategy );
  }

  public void addIndexingParmeters(DirectoryProvider<?> provider, LuceneIndexingParameters indexingParams) {
    dirProviderIndexingParams.put( provider, indexingParams );
  }

  public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider) {
    return dirProviderOptimizerStrategies.get( provider );
  }

  public LuceneIndexingParameters getIndexingParameters(DirectoryProvider<?> provider ) {
    return dirProviderIndexingParams.get( provider );
  }

  public ReaderProvider getReaderProvider() {
    return readerProvider;
  }

  //not happy about having it as a helper class but I don't want cfg to be associated with the SearchFactory
  public static ReflectionManager getReflectionManager(Configuration cfg) {
    ReflectionManager reflectionManager;
    try {
      //TODO introduce a ReflectionManagerHolder interface to avoid reflection
      //I want to avoid hard link between HAN and Validator for usch a simple need
      //reuse the existing reflectionManager one when possible
      reflectionManager =
          (ReflectionManager) cfg.getClass().getMethod( "getReflectionManager" ).invoke( cfg );

    }
    catch (Exception e) {
      reflectionManager = new JavaReflectionManager();
    }
    return reflectionManager;
  }

  public DirectoryProvider[] getDirectoryProviders(Class entity) {
    DocumentBuilder<Object> documentBuilder = getDocumentBuilders().get( entity );
    return documentBuilder == null ? null : documentBuilder.getDirectoryProviders();
  }

  public void optimize() {
    Set<Class> clazzs = getDocumentBuilders().keySet();
    for (Class clazz : clazzs) {
      optimize( clazz );
    }
  }

  public void optimize(Class entityType) {
    if ( ! getDocumentBuilders().containsKey( entityType ) ) {
      throw new SearchException("Entity not indexed: " + entityType);
    }
    List<LuceneWork> queue = new ArrayList<LuceneWork>(1);
    queue.add( new OptimizeLuceneWork( entityType ) );
    getBackendQueueProcessorFactory().getProcessor( queue ).run();
  }

  private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, Analyzer analyzer) {
    Iterator iter = cfg.getClassMappings();
    DirectoryProviderFactory factory = new DirectoryProviderFactory();
    while (iter.hasNext()) {
      PersistentClass clazz = (PersistentClass) iter.next();
      Class<?> mappedClass = clazz.getMappedClass();
      if (mappedClass != null) {
        XClass mappedXClass = reflectionManager.toXClass(mappedClass);
        if ( mappedXClass != null) {
          if ( mappedXClass.isAnnotationPresent( Indexed.class ) ) {
            DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders( mappedXClass, cfg, this );

            final DocumentBuilder<Object> documentBuilder = new DocumentBuilder<Object>(
                mappedXClass, analyzer, providers.getProviders(), providers.getSelectionStrategy(),
                reflectionManager
            );

            documentBuilders.put( mappedClass, documentBuilder );
          }
          bindFilterDefs(mappedXClass);
        }
      }
    }
    factory.startDirectoryProviders();
  }

  /**
   * Initilises the Lucene analyzer to use by reading the analyzer class from the configuration and instantiating it.
   *
   * @param cfg
   *            The current configuration.
   * @return The Lucene analyzer to use for tokenisation.
   */
  private Analyzer initAnalyzer(Configuration cfg) {
    Class analyzerClass;
    String analyzerClassName = cfg.getProperty(Environment.ANALYZER_CLASS);
    if (analyzerClassName != null) {
      try {
        analyzerClass = ReflectHelper.classForName(analyzerClassName);
      } catch (Exception e) {
        throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
            + Environment.ANALYZER_CLASS + "' could not be found.", e);
      }
    } else {
      analyzerClass = StandardAnalyzer.class;
    }
    // Initialize analyzer
    Analyzer defaultAnalyzer;
    try {
      defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
    } catch (ClassCastException e) {
      throw new SearchException("Lucene analyzer does not implement " + Analyzer.class.getName() + ": "
          + analyzerClassName, e);
    } catch (Exception e) {
      throw new SearchException("Failed to instantiate lucene analyzer with type " + analyzerClassName, e);
    }
    return defaultAnalyzer;
  }

  private void buildFilterCachingStrategy(Properties properties) {
    String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
    if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
      filterCachingStrategy = new MRUFilterCachingStrategy();
    }
    else {
      try {
        Class filterCachingStrategyClass = org.hibernate.annotations.common.util.ReflectHelper.classForName( impl, SearchFactoryImpl.class );
        filterCachingStrategy = (FilterCachingStrategy) filterCachingStrategyClass.newInstance();
      }
      catch (ClassNotFoundException e) {
        throw new SearchException( "Unable to find filterCachingStrategy class: " + impl, e );
      }
      catch (IllegalAccessException e) {
        throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
      }
      catch (InstantiationException e) {
        throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
      }
    }
    filterCachingStrategy.initialize( properties );
  }

  public FilterCachingStrategy getFilterCachingStrategy() {
    return filterCachingStrategy;
  }

  public FilterDef getFilterDefinition(String name) {
    return filterDefinitions.get( name );
  }
}
TOP

Related Classes of org.hibernate.search.impl.SearchFactoryImpl

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.