[hibernate-commits] Hibernate SVN: r20834 - in search/trunk/hibernate-search/src: main/java/org/hibernate/search/impl and 4 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Oct 14 13:31:28 EDT 2010


Author: epbernard
Date: 2010-10-14 13:31:27 -0400 (Thu, 14 Oct 2010)
New Revision: 20834

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/spi/SearchFactoryBuilder.java
Removed:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java
Modified:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/event/FullTextIndexEventListener.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/IncrementalSearchConfiguration.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/MutableSearchFactory.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchMappingBuilder.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/configuration/mutablefactory/MutableFactoryTest.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/id/providedId/ProvidedIdTest.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/MutableSearchFactoryAndJMXTest.java
Log:
HSEARCH-555 Properly expose as public API the SearchFactoryBuilder

Move to the SearchFactoryBuilder to the spi package

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/event/FullTextIndexEventListener.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/event/FullTextIndexEventListener.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/event/FullTextIndexEventListener.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -32,6 +32,7 @@
 import javax.transaction.Status;
 import javax.transaction.Synchronization;
 
+import org.hibernate.search.spi.SearchFactoryBuilder;
 import org.slf4j.Logger;
 
 import org.hibernate.Session;
@@ -63,7 +64,6 @@
 import org.hibernate.search.backend.impl.EventSourceTransactionContext;
 import org.hibernate.search.cfg.SearchConfigurationFromHibernateCore;
 import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.impl.SearchFactoryBuilder;
 import org.hibernate.search.util.LoggerFactory;
 import org.hibernate.search.util.ReflectionHelper;
 import org.hibernate.search.util.WeakIdentityHashMap;

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/IncrementalSearchConfiguration.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/IncrementalSearchConfiguration.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/IncrementalSearchConfiguration.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -14,7 +14,7 @@
 /**
  * @author Emmanuel Bernard
  */
-class IncrementalSearchConfiguration implements SearchConfiguration {
+public class IncrementalSearchConfiguration implements SearchConfiguration {
 	private final ReflectionManager reflectionManager = new JavaReflectionManager();
 	private final List<Class<?>> classes;
 	private final Map<String, Class<?>> classesByName = new HashMap<String, Class<?>>();

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/MutableSearchFactory.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/MutableSearchFactory.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/MutableSearchFactory.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -44,6 +44,7 @@
 import org.hibernate.search.filter.FilterCachingStrategy;
 import org.hibernate.search.query.dsl.QueryContextBuilder;
 import org.hibernate.search.reader.ReaderProvider;
+import org.hibernate.search.spi.SearchFactoryBuilder;
 import org.hibernate.search.spi.SearchFactoryIntegrator;
 import org.hibernate.search.spi.internals.DirectoryProviderData;
 import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
@@ -69,7 +70,7 @@
 	//lock to be acquired every time the underlying searchFactory is rebuilt
 	private final Lock mutating = new ReentrantLock();
 
-	void setDelegate(SearchFactoryImplementorWithShareableState delegate) {
+	public void setDelegate(SearchFactoryImplementorWithShareableState delegate) {
 		this.delegate = delegate;
 	}
 

Deleted: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -1,610 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors.  All third-party contributions are
- * distributed under license by Red Hat, Inc.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program 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 distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA  02110-1301  USA
- */
-package org.hibernate.search.impl;
-
-import java.beans.Introspector;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.apache.lucene.search.Similarity;
-import org.hibernate.search.spi.internals.SearchFactoryImplementorWithShareableState;
-import org.hibernate.search.spi.internals.SearchFactoryState;
-import org.slf4j.Logger;
-
-import org.hibernate.annotations.common.reflection.MetadataProvider;
-import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
-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.search.Environment;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.Version;
-import org.hibernate.search.annotations.AnalyzerDef;
-import org.hibernate.search.annotations.AnalyzerDefs;
-import org.hibernate.search.annotations.Factory;
-import org.hibernate.search.annotations.FullTextFilterDef;
-import org.hibernate.search.annotations.FullTextFilterDefs;
-import org.hibernate.search.annotations.Indexed;
-import org.hibernate.search.annotations.Key;
-import org.hibernate.search.backend.BackendQueueProcessorFactory;
-import org.hibernate.search.backend.LuceneIndexingParameters;
-import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
-import org.hibernate.search.backend.WorkerFactory;
-import org.hibernate.search.backend.configuration.ConfigurationParseHelper;
-import org.hibernate.search.cfg.SearchConfiguration;
-import org.hibernate.search.cfg.SearchMapping;
-import org.hibernate.search.engine.DocumentBuilderContainedEntity;
-import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
-import org.hibernate.search.engine.EntityState;
-import org.hibernate.search.engine.FilterDef;
-import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.exception.ErrorHandler;
-import org.hibernate.search.exception.impl.LogErrorHandler;
-import org.hibernate.search.filter.CachingWrapperFilter;
-import org.hibernate.search.filter.FilterCachingStrategy;
-import org.hibernate.search.filter.MRUFilterCachingStrategy;
-import org.hibernate.search.filter.ShardSensitiveOnlyFilter;
-import org.hibernate.search.jmx.IndexControl;
-import org.hibernate.search.jmx.JMXRegistrar;
-import org.hibernate.search.reader.ReaderProviderFactory;
-import org.hibernate.search.spi.WorkerBuildContext;
-import org.hibernate.search.spi.WritableBuildContext;
-import org.hibernate.search.spi.internals.DirectoryProviderData;
-import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
-import org.hibernate.search.store.DirectoryProvider;
-import org.hibernate.search.store.DirectoryProviderFactory;
-import org.hibernate.search.store.optimization.OptimizerStrategy;
-import org.hibernate.search.util.ClassLoaderHelper;
-import org.hibernate.search.util.LoggerFactory;
-import org.hibernate.search.util.ReflectionHelper;
-
-/**
- * Build a search factory following the builder pattern.
- *
- * @author Emmanuel Bernard
- * @author Hardy Ferentschik
- */
-public class SearchFactoryBuilder {
-
-	static {
-		Version.touch();
-	}
-
-	private static final Logger log = LoggerFactory.make();
-	private SearchConfiguration cfg;
-	private MutableSearchFactory rootFactory;
-	private final List<Class<?>> classes = new ArrayList<Class<?>>();
-
-	public SearchFactoryBuilder configuration(SearchConfiguration configuration) {
-		this.cfg = configuration;
-		return this;
-	}
-
-	public SearchFactoryBuilder rootFactory(MutableSearchFactory factory) {
-		this.rootFactory = factory;
-		return this;
-	}
-
-	public SearchFactoryBuilder addClass(Class<?> clazz) {
-		classes.add( clazz );
-		return this;
-	}
-
-	private final MutableSearchFactoryState factoryState = new MutableSearchFactoryState();
-
-	public SearchFactoryImplementor buildSearchFactory() {
-		SearchFactoryImplementor searchFactoryImplementor;
-		if ( rootFactory == null ) {
-			if ( classes.size() > 0 ) {
-				throw new SearchException( "Cannot add a class if the original SearchFactory is not passed" );
-			}
-			searchFactoryImplementor = buildNewSearchFactory();
-		}
-		else {
-			searchFactoryImplementor = buildIncrementalSearchFactory();
-		}
-
-		String enableJMX = factoryState.getConfigurationProperties().getProperty( Environment.JMX_ENABLED );
-		if ( "true".equalsIgnoreCase( enableJMX ) ) {
-			enableIndexControlBean( searchFactoryImplementor );
-		}
-		return searchFactoryImplementor;
-	}
-
-	private void enableIndexControlBean(SearchFactoryImplementor searchFactoryImplementor) {
-		if ( !searchFactoryImplementor.isJMXEnabled() ) {
-			return;
-		}
-		final Properties configurationProperties = factoryState.getConfigurationProperties();
-
-		// if we don't have a JNDI bound SessionFactory we cannot enable the index control bean
-		if ( StringHelper.isEmpty( configurationProperties.getProperty( "hibernate.session_factory_name" ) ) ) {
-			log.debug(
-					"In order to bind the IndexControlMBean the Hibernate SessionFactory has to be available via JNDI"
-			);
-			return;
-		}
-
-		// since the SearchFactory is mutable we might have an already existing MBean which we have to unregister first
-		if ( JMXRegistrar.isNameRegistered( IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME ) ) {
-			JMXRegistrar.unRegisterMBean( IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME );
-		}
-
-		IndexControl indexCtrlBean = new IndexControl( configurationProperties );
-		JMXRegistrar.registerMBean( indexCtrlBean, IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME );
-	}
-
-	private SearchFactoryImplementor buildIncrementalSearchFactory() {
-		removeClassesAlreadyManaged();
-		if ( classes.size() == 0 ) {
-			return rootFactory;
-		}
-		final Properties configurationProperties = factoryState.getConfigurationProperties();
-		BuildContext buildContext = new BuildContext();
-		factoryState.copyStateFromOldFactory( rootFactory );
-		//TODO we don't keep the reflectionManager. Is that an issue?
-		IncrementalSearchConfiguration cfg = new IncrementalSearchConfiguration( classes, configurationProperties );
-		final ReflectionManager reflectionManager = getReflectionManager( cfg );
-
-		//TODO programmatic mapping support
-		//FIXME The current initDocumentBuilders
-		initDocumentBuilders( cfg, reflectionManager, buildContext );
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
-		Set<Class<?>> indexedClasses = documentBuildersIndexedEntities.keySet();
-		for ( DocumentBuilderIndexedEntity builder : documentBuildersIndexedEntities.values() ) {
-			//FIXME improve this algorithm to deal with adding new classes to the class hierarchy.
-			//Today it seems only safe when a class outside the hierarchy is incrementally added.
-			builder.postInitialize( indexedClasses );
-		}
-		//not really necessary today
-		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
-		for ( DocumentBuilderContainedEntity builder : documentBuildersContainedEntities.values() ) {
-			builder.postInitialize( indexedClasses );
-		}
-		fillSimilarityMapping();
-
-		//update backend
-		final BackendQueueProcessorFactory backend = factoryState.getBackendQueueProcessorFactory();
-		if ( backend instanceof UpdatableBackendQueueProcessorFactory ) {
-			final UpdatableBackendQueueProcessorFactory updatableBackend = ( UpdatableBackendQueueProcessorFactory ) backend;
-			updatableBackend.updateDirectoryProviders( factoryState.getDirectoryProviderData().keySet(), buildContext );
-		}
-		//safe for incremental init at least the ShredBufferReaderProvider
-		//this.readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
-		SearchFactoryImplementorWithShareableState factory = new ImmutableSearchFactory( factoryState );
-		rootFactory.setDelegate( factory );
-		return rootFactory;
-
-
-	}
-
-	private void removeClassesAlreadyManaged() {
-		Set<Class<?>> remove = new HashSet<Class<?>>();
-		final Map<Class<?>, DocumentBuilderContainedEntity<?>> containedEntities = rootFactory.getDocumentBuildersContainedEntities();
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> indexedEntities = rootFactory.getDocumentBuildersIndexedEntities();
-		for ( Class<?> entity : classes ) {
-			if ( indexedEntities.containsKey( entity ) || containedEntities.containsKey( entity ) ) {
-				remove.add( entity );
-			}
-		}
-		for ( Class<?> entity : remove ) {
-			classes.remove( entity );
-		}
-	}
-
-	private SearchFactoryImplementor buildNewSearchFactory() {
-		createCleanFactoryState();
-
-		factoryState.setConfigurationProperties( cfg.getProperties() );
-		factoryState.setErrorHandler( createErrorHandler( factoryState.getConfigurationProperties() ) );
-		final ReflectionManager reflectionManager = getReflectionManager( cfg );
-		BuildContext buildContext = new BuildContext();
-
-		final SearchMapping mapping = SearchMappingBuilder.getSearchMapping( cfg );
-		if ( mapping != null ) {
-			if ( !( reflectionManager instanceof MetadataProviderInjector ) ) {
-				throw new SearchException(
-						"Programmatic mapping model used but ReflectionManager does not implement "
-								+ MetadataProviderInjector.class.getName()
-				);
-			}
-			MetadataProviderInjector injector = ( MetadataProviderInjector ) reflectionManager;
-			MetadataProvider original = injector.getMetadataProvider();
-			injector.setMetadataProvider( new MappingModelMetadataProvider( original, mapping ) );
-		}
-
-		factoryState.setIndexingStrategy( defineIndexingStrategy( cfg ) );//need to be done before the document builds
-		factoryState.setDirectoryProviderIndexingParams( new HashMap<DirectoryProvider, LuceneIndexingParameters>() );
-		initDocumentBuilders( cfg, reflectionManager, buildContext );
-
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
-		Set<Class<?>> indexedClasses = documentBuildersIndexedEntities.keySet();
-		for ( DocumentBuilderIndexedEntity builder : documentBuildersIndexedEntities.values() ) {
-			builder.postInitialize( indexedClasses );
-		}
-		//not really necessary today
-		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
-		for ( DocumentBuilderContainedEntity builder : documentBuildersContainedEntities.values() ) {
-			builder.postInitialize( indexedClasses );
-		}
-		fillSimilarityMapping();
-
-		//build back end
-		factoryState.setWorker( WorkerFactory.createWorker( cfg, buildContext ) );
-		factoryState.setReaderProvider( ReaderProviderFactory.createReaderProvider( cfg, buildContext ) );
-		factoryState.setFilterCachingStrategy( buildFilterCachingStrategy( cfg.getProperties() ) );
-		factoryState.setCacheBitResultsSize(
-				ConfigurationParseHelper.getIntValue(
-					cfg.getProperties(), Environment.CACHE_DOCIDRESULTS_SIZE, CachingWrapperFilter.DEFAULT_SIZE
-				)
-		);
-		SearchFactoryImplementorWithShareableState factory = new ImmutableSearchFactory( factoryState );
-		rootFactory.setDelegate( factory );
-		return rootFactory;
-	}
-
-	private void fillSimilarityMapping() {
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
-		for ( DirectoryProviderData directoryConfiguration : factoryState.getDirectoryProviderData().values() ) {
-			for ( Class<?> indexedType : directoryConfiguration.getClasses() ) {
-				DocumentBuilderIndexedEntity<?> documentBuilder = documentBuildersIndexedEntities.get( indexedType );
-				Similarity similarity = documentBuilder.getSimilarity();
-				Similarity prevSimilarity = directoryConfiguration.getSimilarity();
-				if ( prevSimilarity != null && !prevSimilarity.getClass().equals( similarity.getClass() ) ) {
-					throw new SearchException(
-							"Multiple entities are sharing the same index but are declaring an " +
-									"inconsistent Similarity. When overrriding default Similarity make sure that all types sharing a same index " +
-									"declare the same Similarity implementation."
-					);
-				}
-				else {
-					directoryConfiguration.setSimilarity( similarity );
-				}
-			}
-		}
-	}
-
-	private static FilterCachingStrategy buildFilterCachingStrategy(Properties properties) {
-		FilterCachingStrategy filterCachingStrategy;
-		String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
-		if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
-			filterCachingStrategy = new MRUFilterCachingStrategy();
-		}
-		else {
-			filterCachingStrategy = ClassLoaderHelper.instanceFromName(
-					FilterCachingStrategy.class,
-					impl, ImmutableSearchFactory.class, "filterCachingStrategy"
-			);
-		}
-		filterCachingStrategy.initialize( properties );
-		return filterCachingStrategy;
-	}
-
-	private void createCleanFactoryState() {
-		if ( rootFactory == null ) {
-			//set the mutable structure of factory state
-			rootFactory = new MutableSearchFactory();
-			factoryState.setDocumentBuildersIndexedEntities( new HashMap<Class<?>, DocumentBuilderIndexedEntity<?>>() );
-			factoryState.setDocumentBuildersContainedEntities( new HashMap<Class<?>, DocumentBuilderContainedEntity<?>>() );
-			factoryState.setDirectoryProviderData( new HashMap<DirectoryProvider<?>, DirectoryProviderData>() );
-			factoryState.setFilterDefinitions( new HashMap<String, FilterDef>() );
-			factoryState.setIndexHierarchy( new PolymorphicIndexHierarchy() );
-		}
-	}
-
-	/*
-	 * Initialize the document builder
-	 * This algorithm seems to be safe for incremental search factories.
-	 */
-
-	private void initDocumentBuilders(SearchConfiguration cfg, ReflectionManager reflectionManager, BuildContext buildContext) {
-		ConfigContext context = new ConfigContext( cfg );
-		Iterator<Class<?>> iter = cfg.getClassMappings();
-		DirectoryProviderFactory factory = new DirectoryProviderFactory();
-
-		initProgrammaticAnalyzers( context, reflectionManager );
-		initProgrammaticallyDefinedFilterDef( reflectionManager );
-		final PolymorphicIndexHierarchy indexingHierarchy = factoryState.getIndexHierarchy();
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
-		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
-		while ( iter.hasNext() ) {
-			Class<?> mappedClass = iter.next();
-			if ( mappedClass == null ) {
-				continue;
-			}
-			@SuppressWarnings("unchecked")
-			XClass mappedXClass = reflectionManager.toXClass( mappedClass );
-			if ( mappedXClass == null ) {
-				continue;
-			}
-
-			if ( mappedXClass.isAnnotationPresent( Indexed.class ) ) {
-
-				if ( mappedXClass.isAbstract() ) {
-					log.warn( "Abstract classes can never insert index documents. Remove @Indexed." );
-					continue;
-				}
-
-				DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders(
-						mappedXClass, cfg, buildContext, reflectionManager
-				);
-				//FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
-				//XClass unfortunately is not (yet) genericized: TODO?
-				final DocumentBuilderIndexedEntity<?> documentBuilder = new DocumentBuilderIndexedEntity(
-						mappedXClass, context, providers.getProviders(), providers.getSelectionStrategy(),
-						reflectionManager
-				);
-
-				indexingHierarchy.addIndexedClass( mappedClass );
-				documentBuildersIndexedEntities.put( mappedClass, documentBuilder );
-			}
-			else {
-				//FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
-				//XClass unfortunately is not (yet) genericized: TODO?
-				final DocumentBuilderContainedEntity<?> documentBuilder = new DocumentBuilderContainedEntity(
-						mappedXClass, context, reflectionManager
-				);
-				//TODO enhance that, I don't like to expose EntityState
-				if ( documentBuilder.getEntityState() != EntityState.NON_INDEXABLE ) {
-					documentBuildersContainedEntities.put( mappedClass, documentBuilder );
-				}
-			}
-			bindFilterDefs( mappedXClass );
-			//TODO should analyzer def for classes at tyher sqme level???
-		}
-		factoryState.setAnalyzers( context.initLazyAnalyzers() );
-		factory.startDirectoryProviders();
-	}
-
-	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 ( factoryState.getFilterDefinitions().containsKey( defAnn.name() ) ) {
-			throw new SearchException(
-					"Multiple definition of @FullTextFilterDef.name=" + defAnn.name() + ": "
-							+ mappedXClass.getName()
-			);
-		}
-
-		bindFullTextFilterDef( defAnn );
-	}
-
-	private void bindFullTextFilterDef(FullTextFilterDef defAnn) {
-		FilterDef filterDef = new FilterDef( defAnn );
-		final Map<String, FilterDef> filterDefinition = factoryState.getFilterDefinitions();
-		if ( filterDef.getImpl().equals( ShardSensitiveOnlyFilter.class ) ) {
-			//this is a placeholder don't process regularly
-			filterDefinition.put( defAnn.name(), filterDef );
-			return;
-		}
-		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()
-					);
-				}
-				ReflectionHelper.setAccessible( method );
-				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()
-					);
-				}
-				ReflectionHelper.setAccessible( method );
-				filterDef.setKeyMethod( method );
-			}
-
-			String name = method.getName();
-			if ( name.startsWith( "set" ) && method.getParameterTypes().length == 1 ) {
-				filterDef.addSetter( Introspector.decapitalize( name.substring( 3 ) ), method );
-			}
-		}
-		filterDefinition.put( defAnn.name(), filterDef );
-	}
-
-	private void initProgrammaticAnalyzers(ConfigContext context, ReflectionManager reflectionManager) {
-		final Map defaults = reflectionManager.getDefaults();
-
-		if ( defaults != null ) {
-			AnalyzerDef[] defs = ( AnalyzerDef[] ) defaults.get( AnalyzerDefs.class );
-			if ( defs != null ) {
-				for ( AnalyzerDef def : defs ) {
-					context.addAnalyzerDef( def );
-				}
-			}
-		}
-	}
-
-	private void initProgrammaticallyDefinedFilterDef(ReflectionManager reflectionManager) {
-		@SuppressWarnings("unchecked") Map defaults = reflectionManager.getDefaults();
-		FullTextFilterDef[] filterDefs = ( FullTextFilterDef[] ) defaults.get( FullTextFilterDefs.class );
-		if ( filterDefs != null && filterDefs.length != 0 ) {
-			final Map<String, FilterDef> filterDefinitions = factoryState.getFilterDefinitions();
-			for ( FullTextFilterDef defAnn : filterDefs ) {
-				if ( filterDefinitions.containsKey( defAnn.name() ) ) {
-					throw new SearchException( "Multiple definition of @FullTextFilterDef.name=" + defAnn.name() );
-				}
-				bindFullTextFilterDef( defAnn );
-			}
-		}
-	}
-
-	private static ErrorHandler createErrorHandler(Properties configuration) {
-		String errorHandlerClassName = configuration.getProperty( Environment.ERROR_HANDLER );
-		if ( StringHelper.isEmpty( errorHandlerClassName ) ) {
-			return new LogErrorHandler();
-		}
-		else if ( errorHandlerClassName.trim().equals( "log" ) ) {
-			return new LogErrorHandler();
-		}
-		else {
-			return ClassLoaderHelper.instanceFromName(
-					ErrorHandler.class, errorHandlerClassName,
-					ImmutableSearchFactory.class, "Error Handler"
-			);
-		}
-	}
-
-	private ReflectionManager getReflectionManager(SearchConfiguration cfg) {
-		ReflectionManager reflectionManager = cfg.getReflectionManager();
-		return geReflectionManager( reflectionManager );
-	}
-
-	private ReflectionManager geReflectionManager(ReflectionManager reflectionManager) {
-		if ( reflectionManager == null ) {
-			reflectionManager = new JavaReflectionManager();
-		}
-		return reflectionManager;
-	}
-
-	private static String defineIndexingStrategy(SearchConfiguration cfg) {
-		String indexingStrategy = cfg.getProperties().getProperty( Environment.INDEXING_STRATEGY, "event" );
-		if ( !( "event".equals( indexingStrategy ) || "manual".equals( indexingStrategy ) ) ) {
-			throw new SearchException( Environment.INDEXING_STRATEGY + " unknown: " + indexingStrategy );
-		}
-		return indexingStrategy;
-	}
-
-	/**
-	 * Implementation of the Hibernate Search SPI WritableBuildContext and WorkerBuildContext
-	 * The data is provided by the SearchFactoryState object associated to SearchFactoryBuilder.
-	 */
-	private class BuildContext implements WritableBuildContext, WorkerBuildContext {
-		private final SearchFactoryState factoryState = SearchFactoryBuilder.this.factoryState;
-
-		public void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy optimizerStrategy) {
-			final Map<DirectoryProvider<?>, DirectoryProviderData> dirProviderData = factoryState.getDirectoryProviderData();
-			DirectoryProviderData data = dirProviderData.get( provider );
-			if ( data == null ) {
-				data = new DirectoryProviderData();
-				dirProviderData.put( provider, data );
-			}
-			data.setOptimizerStrategy( optimizerStrategy );
-		}
-
-		public void addIndexingParameters(DirectoryProvider<?> provider, LuceneIndexingParameters indexingParams) {
-			factoryState.getDirectoryProviderIndexingParams().put( provider, indexingParams );
-		}
-
-		public void addClassToDirectoryProvider(Class<?> entity, DirectoryProvider<?> directoryProvider, boolean exclusiveIndexUsage) {
-			final Map<DirectoryProvider<?>, DirectoryProviderData> dirProviderData = factoryState.getDirectoryProviderData();
-			DirectoryProviderData data = dirProviderData.get( directoryProvider );
-			if ( data == null ) {
-				data = new DirectoryProviderData();
-				dirProviderData.put( directoryProvider, data );
-			}
-			data.getClasses().add( entity );
-			data.setExclusiveIndexUsage( exclusiveIndexUsage );
-		}
-
-		public SearchFactoryImplementor getUninitializedSearchFactory() {
-			return rootFactory;
-		}
-
-		public String getIndexingStrategy() {
-			return factoryState.getIndexingStrategy();
-		}
-
-		public Set<DirectoryProvider<?>> getDirectoryProviders() {
-			return factoryState.getDirectoryProviderData().keySet();
-		}
-
-		public void setBackendQueueProcessorFactory(BackendQueueProcessorFactory backendQueueProcessorFactory) {
-			factoryState.setBackendQueueProcessorFactory( backendQueueProcessorFactory );
-		}
-
-		public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider) {
-			return factoryState.getDirectoryProviderData().get( provider ).getOptimizerStrategy();
-		}
-
-		public Set<Class<?>> getClassesInDirectoryProvider(DirectoryProvider<?> directoryProvider) {
-			return Collections.unmodifiableSet( factoryState.getDirectoryProviderData().get( directoryProvider ).getClasses() );
-		}
-
-		public LuceneIndexingParameters getIndexingParameters(DirectoryProvider<?> provider) {
-			return factoryState.getDirectoryProviderIndexingParams().get( provider );
-		}
-
-		public ReentrantLock getDirectoryProviderLock(DirectoryProvider<?> dp) {
-			return factoryState.getDirectoryProviderData().get( dp ).getDirLock();
-		}
-
-		public Similarity getSimilarity(DirectoryProvider<?> provider) {
-			Similarity similarity = factoryState.getDirectoryProviderData().get( provider ).getSimilarity();
-			if ( similarity == null ) {
-				throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
-			}
-			return similarity;
-		}
-
-		public boolean isExclusiveIndexUsageEnabled(DirectoryProvider<?> provider) {
-			return factoryState.getDirectoryProviderData().get( provider ).isExclusiveIndexUsage();
-		}
-
-		public ErrorHandler getErrorHandler() {
-			return factoryState.getErrorHandler();
-		}
-
-		@SuppressWarnings("unchecked")
-		public <T> DocumentBuilderIndexedEntity<T> getDocumentBuilderIndexedEntity(Class<T> entityType) {
-			return ( DocumentBuilderIndexedEntity<T> ) factoryState.getDocumentBuildersIndexedEntities().get( entityType );
-		}
-
-	}
-}

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchMappingBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchMappingBuilder.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchMappingBuilder.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -40,7 +40,7 @@
 /**
  * package class extracting the SearchMappingFactory if needed
  */
-class SearchMappingBuilder {
+public class SearchMappingBuilder {
 	private static final Logger LOG = LoggerFactory.make();
 
 	private SearchMappingBuilder() {

Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/spi/SearchFactoryBuilder.java (from rev 20833, search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java)
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/spi/SearchFactoryBuilder.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/spi/SearchFactoryBuilder.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -0,0 +1,617 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.spi;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.lucene.search.Similarity;
+import org.hibernate.search.impl.ConfigContext;
+import org.hibernate.search.impl.ImmutableSearchFactory;
+import org.hibernate.search.impl.IncrementalSearchConfiguration;
+import org.hibernate.search.impl.MappingModelMetadataProvider;
+import org.hibernate.search.impl.MutableSearchFactory;
+import org.hibernate.search.impl.MutableSearchFactoryState;
+import org.hibernate.search.impl.SearchMappingBuilder;
+import org.hibernate.search.spi.internals.SearchFactoryImplementorWithShareableState;
+import org.hibernate.search.spi.internals.SearchFactoryState;
+import org.slf4j.Logger;
+
+import org.hibernate.annotations.common.reflection.MetadataProvider;
+import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
+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.search.Environment;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.Version;
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.AnalyzerDefs;
+import org.hibernate.search.annotations.Factory;
+import org.hibernate.search.annotations.FullTextFilterDef;
+import org.hibernate.search.annotations.FullTextFilterDefs;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.Key;
+import org.hibernate.search.backend.BackendQueueProcessorFactory;
+import org.hibernate.search.backend.LuceneIndexingParameters;
+import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
+import org.hibernate.search.backend.WorkerFactory;
+import org.hibernate.search.backend.configuration.ConfigurationParseHelper;
+import org.hibernate.search.cfg.SearchConfiguration;
+import org.hibernate.search.cfg.SearchMapping;
+import org.hibernate.search.engine.DocumentBuilderContainedEntity;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
+import org.hibernate.search.engine.EntityState;
+import org.hibernate.search.engine.FilterDef;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.exception.ErrorHandler;
+import org.hibernate.search.exception.impl.LogErrorHandler;
+import org.hibernate.search.filter.CachingWrapperFilter;
+import org.hibernate.search.filter.FilterCachingStrategy;
+import org.hibernate.search.filter.MRUFilterCachingStrategy;
+import org.hibernate.search.filter.ShardSensitiveOnlyFilter;
+import org.hibernate.search.jmx.IndexControl;
+import org.hibernate.search.jmx.JMXRegistrar;
+import org.hibernate.search.reader.ReaderProviderFactory;
+import org.hibernate.search.spi.WorkerBuildContext;
+import org.hibernate.search.spi.WritableBuildContext;
+import org.hibernate.search.spi.internals.DirectoryProviderData;
+import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
+import org.hibernate.search.store.DirectoryProvider;
+import org.hibernate.search.store.DirectoryProviderFactory;
+import org.hibernate.search.store.optimization.OptimizerStrategy;
+import org.hibernate.search.util.ClassLoaderHelper;
+import org.hibernate.search.util.LoggerFactory;
+import org.hibernate.search.util.ReflectionHelper;
+
+/**
+ * Build a search factory following the builder pattern.
+ *
+ * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
+ */
+public class SearchFactoryBuilder {
+
+	static {
+		Version.touch();
+	}
+
+	private static final Logger log = LoggerFactory.make();
+	private SearchConfiguration cfg;
+	private MutableSearchFactory rootFactory;
+	private final List<Class<?>> classes = new ArrayList<Class<?>>();
+
+	public SearchFactoryBuilder configuration(SearchConfiguration configuration) {
+		this.cfg = configuration;
+		return this;
+	}
+
+	public SearchFactoryBuilder rootFactory(MutableSearchFactory factory) {
+		this.rootFactory = factory;
+		return this;
+	}
+
+	public SearchFactoryBuilder addClass(Class<?> clazz) {
+		classes.add( clazz );
+		return this;
+	}
+
+	private final MutableSearchFactoryState factoryState = new MutableSearchFactoryState();
+
+	public SearchFactoryImplementor buildSearchFactory() {
+		SearchFactoryImplementor searchFactoryImplementor;
+		if ( rootFactory == null ) {
+			if ( classes.size() > 0 ) {
+				throw new SearchException( "Cannot add a class if the original SearchFactory is not passed" );
+			}
+			searchFactoryImplementor = buildNewSearchFactory();
+		}
+		else {
+			searchFactoryImplementor = buildIncrementalSearchFactory();
+		}
+
+		String enableJMX = factoryState.getConfigurationProperties().getProperty( Environment.JMX_ENABLED );
+		if ( "true".equalsIgnoreCase( enableJMX ) ) {
+			enableIndexControlBean( searchFactoryImplementor );
+		}
+		return searchFactoryImplementor;
+	}
+
+	private void enableIndexControlBean(SearchFactoryImplementor searchFactoryImplementor) {
+		if ( !searchFactoryImplementor.isJMXEnabled() ) {
+			return;
+		}
+		final Properties configurationProperties = factoryState.getConfigurationProperties();
+
+		// if we don't have a JNDI bound SessionFactory we cannot enable the index control bean
+		if ( StringHelper.isEmpty( configurationProperties.getProperty( "hibernate.session_factory_name" ) ) ) {
+			log.debug(
+					"In order to bind the IndexControlMBean the Hibernate SessionFactory has to be available via JNDI"
+			);
+			return;
+		}
+
+		// since the SearchFactory is mutable we might have an already existing MBean which we have to unregister first
+		if ( JMXRegistrar.isNameRegistered( IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME ) ) {
+			JMXRegistrar.unRegisterMBean( IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME );
+		}
+
+		IndexControl indexCtrlBean = new IndexControl( configurationProperties );
+		JMXRegistrar.registerMBean( indexCtrlBean, IndexControl.INDEX_CTRL_MBEAN_OBJECT_NAME );
+	}
+
+	private SearchFactoryImplementor buildIncrementalSearchFactory() {
+		removeClassesAlreadyManaged();
+		if ( classes.size() == 0 ) {
+			return rootFactory;
+		}
+		final Properties configurationProperties = factoryState.getConfigurationProperties();
+		BuildContext buildContext = new BuildContext();
+		factoryState.copyStateFromOldFactory( rootFactory );
+		//TODO we don't keep the reflectionManager. Is that an issue?
+		IncrementalSearchConfiguration cfg = new IncrementalSearchConfiguration( classes, configurationProperties );
+		final ReflectionManager reflectionManager = getReflectionManager( cfg );
+
+		//TODO programmatic mapping support
+		//FIXME The current initDocumentBuilders
+		initDocumentBuilders( cfg, reflectionManager, buildContext );
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
+		Set<Class<?>> indexedClasses = documentBuildersIndexedEntities.keySet();
+		for ( DocumentBuilderIndexedEntity builder : documentBuildersIndexedEntities.values() ) {
+			//FIXME improve this algorithm to deal with adding new classes to the class hierarchy.
+			//Today it seems only safe when a class outside the hierarchy is incrementally added.
+			builder.postInitialize( indexedClasses );
+		}
+		//not really necessary today
+		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
+		for ( DocumentBuilderContainedEntity builder : documentBuildersContainedEntities.values() ) {
+			builder.postInitialize( indexedClasses );
+		}
+		fillSimilarityMapping();
+
+		//update backend
+		final BackendQueueProcessorFactory backend = factoryState.getBackendQueueProcessorFactory();
+		if ( backend instanceof UpdatableBackendQueueProcessorFactory ) {
+			final UpdatableBackendQueueProcessorFactory updatableBackend = ( UpdatableBackendQueueProcessorFactory ) backend;
+			updatableBackend.updateDirectoryProviders( factoryState.getDirectoryProviderData().keySet(), buildContext );
+		}
+		//safe for incremental init at least the ShredBufferReaderProvider
+		//this.readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
+		SearchFactoryImplementorWithShareableState factory = new ImmutableSearchFactory( factoryState );
+		rootFactory.setDelegate( factory );
+		return rootFactory;
+
+
+	}
+
+	private void removeClassesAlreadyManaged() {
+		Set<Class<?>> remove = new HashSet<Class<?>>();
+		final Map<Class<?>, DocumentBuilderContainedEntity<?>> containedEntities = rootFactory.getDocumentBuildersContainedEntities();
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> indexedEntities = rootFactory.getDocumentBuildersIndexedEntities();
+		for ( Class<?> entity : classes ) {
+			if ( indexedEntities.containsKey( entity ) || containedEntities.containsKey( entity ) ) {
+				remove.add( entity );
+			}
+		}
+		for ( Class<?> entity : remove ) {
+			classes.remove( entity );
+		}
+	}
+
+	private SearchFactoryImplementor buildNewSearchFactory() {
+		createCleanFactoryState();
+
+		factoryState.setConfigurationProperties( cfg.getProperties() );
+		factoryState.setErrorHandler( createErrorHandler( factoryState.getConfigurationProperties() ) );
+		final ReflectionManager reflectionManager = getReflectionManager( cfg );
+		BuildContext buildContext = new BuildContext();
+
+		final SearchMapping mapping = SearchMappingBuilder.getSearchMapping( cfg );
+		if ( mapping != null ) {
+			if ( !( reflectionManager instanceof MetadataProviderInjector ) ) {
+				throw new SearchException(
+						"Programmatic mapping model used but ReflectionManager does not implement "
+								+ MetadataProviderInjector.class.getName()
+				);
+			}
+			MetadataProviderInjector injector = ( MetadataProviderInjector ) reflectionManager;
+			MetadataProvider original = injector.getMetadataProvider();
+			injector.setMetadataProvider( new MappingModelMetadataProvider( original, mapping ) );
+		}
+
+		factoryState.setIndexingStrategy( defineIndexingStrategy( cfg ) );//need to be done before the document builds
+		factoryState.setDirectoryProviderIndexingParams( new HashMap<DirectoryProvider, LuceneIndexingParameters>() );
+		initDocumentBuilders( cfg, reflectionManager, buildContext );
+
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
+		Set<Class<?>> indexedClasses = documentBuildersIndexedEntities.keySet();
+		for ( DocumentBuilderIndexedEntity builder : documentBuildersIndexedEntities.values() ) {
+			builder.postInitialize( indexedClasses );
+		}
+		//not really necessary today
+		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
+		for ( DocumentBuilderContainedEntity builder : documentBuildersContainedEntities.values() ) {
+			builder.postInitialize( indexedClasses );
+		}
+		fillSimilarityMapping();
+
+		//build back end
+		factoryState.setWorker( WorkerFactory.createWorker( cfg, buildContext ) );
+		factoryState.setReaderProvider( ReaderProviderFactory.createReaderProvider( cfg, buildContext ) );
+		factoryState.setFilterCachingStrategy( buildFilterCachingStrategy( cfg.getProperties() ) );
+		factoryState.setCacheBitResultsSize(
+				ConfigurationParseHelper.getIntValue(
+					cfg.getProperties(), Environment.CACHE_DOCIDRESULTS_SIZE, CachingWrapperFilter.DEFAULT_SIZE
+				)
+		);
+		SearchFactoryImplementorWithShareableState factory = new ImmutableSearchFactory( factoryState );
+		rootFactory.setDelegate( factory );
+		return rootFactory;
+	}
+
+	private void fillSimilarityMapping() {
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
+		for ( DirectoryProviderData directoryConfiguration : factoryState.getDirectoryProviderData().values() ) {
+			for ( Class<?> indexedType : directoryConfiguration.getClasses() ) {
+				DocumentBuilderIndexedEntity<?> documentBuilder = documentBuildersIndexedEntities.get( indexedType );
+				Similarity similarity = documentBuilder.getSimilarity();
+				Similarity prevSimilarity = directoryConfiguration.getSimilarity();
+				if ( prevSimilarity != null && !prevSimilarity.getClass().equals( similarity.getClass() ) ) {
+					throw new SearchException(
+							"Multiple entities are sharing the same index but are declaring an " +
+									"inconsistent Similarity. When overrriding default Similarity make sure that all types sharing a same index " +
+									"declare the same Similarity implementation."
+					);
+				}
+				else {
+					directoryConfiguration.setSimilarity( similarity );
+				}
+			}
+		}
+	}
+
+	private static FilterCachingStrategy buildFilterCachingStrategy(Properties properties) {
+		FilterCachingStrategy filterCachingStrategy;
+		String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
+		if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
+			filterCachingStrategy = new MRUFilterCachingStrategy();
+		}
+		else {
+			filterCachingStrategy = ClassLoaderHelper.instanceFromName(
+					FilterCachingStrategy.class,
+					impl, ImmutableSearchFactory.class, "filterCachingStrategy"
+			);
+		}
+		filterCachingStrategy.initialize( properties );
+		return filterCachingStrategy;
+	}
+
+	private void createCleanFactoryState() {
+		if ( rootFactory == null ) {
+			//set the mutable structure of factory state
+			rootFactory = new MutableSearchFactory();
+			factoryState.setDocumentBuildersIndexedEntities( new HashMap<Class<?>, DocumentBuilderIndexedEntity<?>>() );
+			factoryState.setDocumentBuildersContainedEntities( new HashMap<Class<?>, DocumentBuilderContainedEntity<?>>() );
+			factoryState.setDirectoryProviderData( new HashMap<DirectoryProvider<?>, DirectoryProviderData>() );
+			factoryState.setFilterDefinitions( new HashMap<String, FilterDef>() );
+			factoryState.setIndexHierarchy( new PolymorphicIndexHierarchy() );
+		}
+	}
+
+	/*
+	 * Initialize the document builder
+	 * This algorithm seems to be safe for incremental search factories.
+	 */
+
+	private void initDocumentBuilders(SearchConfiguration cfg, ReflectionManager reflectionManager, BuildContext buildContext) {
+		ConfigContext context = new ConfigContext( cfg );
+		Iterator<Class<?>> iter = cfg.getClassMappings();
+		DirectoryProviderFactory factory = new DirectoryProviderFactory();
+
+		initProgrammaticAnalyzers( context, reflectionManager );
+		initProgrammaticallyDefinedFilterDef( reflectionManager );
+		final PolymorphicIndexHierarchy indexingHierarchy = factoryState.getIndexHierarchy();
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = factoryState.getDocumentBuildersIndexedEntities();
+		final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = factoryState.getDocumentBuildersContainedEntities();
+		while ( iter.hasNext() ) {
+			Class<?> mappedClass = iter.next();
+			if ( mappedClass == null ) {
+				continue;
+			}
+			@SuppressWarnings("unchecked")
+			XClass mappedXClass = reflectionManager.toXClass( mappedClass );
+			if ( mappedXClass == null ) {
+				continue;
+			}
+
+			if ( mappedXClass.isAnnotationPresent( Indexed.class ) ) {
+
+				if ( mappedXClass.isAbstract() ) {
+					log.warn( "Abstract classes can never insert index documents. Remove @Indexed." );
+					continue;
+				}
+
+				DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders(
+						mappedXClass, cfg, buildContext, reflectionManager
+				);
+				//FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
+				//XClass unfortunately is not (yet) genericized: TODO?
+				final DocumentBuilderIndexedEntity<?> documentBuilder = new DocumentBuilderIndexedEntity(
+						mappedXClass, context, providers.getProviders(), providers.getSelectionStrategy(),
+						reflectionManager
+				);
+
+				indexingHierarchy.addIndexedClass( mappedClass );
+				documentBuildersIndexedEntities.put( mappedClass, documentBuilder );
+			}
+			else {
+				//FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
+				//XClass unfortunately is not (yet) genericized: TODO?
+				final DocumentBuilderContainedEntity<?> documentBuilder = new DocumentBuilderContainedEntity(
+						mappedXClass, context, reflectionManager
+				);
+				//TODO enhance that, I don't like to expose EntityState
+				if ( documentBuilder.getEntityState() != EntityState.NON_INDEXABLE ) {
+					documentBuildersContainedEntities.put( mappedClass, documentBuilder );
+				}
+			}
+			bindFilterDefs( mappedXClass );
+			//TODO should analyzer def for classes at tyher sqme level???
+		}
+		factoryState.setAnalyzers( context.initLazyAnalyzers() );
+		factory.startDirectoryProviders();
+	}
+
+	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 ( factoryState.getFilterDefinitions().containsKey( defAnn.name() ) ) {
+			throw new SearchException(
+					"Multiple definition of @FullTextFilterDef.name=" + defAnn.name() + ": "
+							+ mappedXClass.getName()
+			);
+		}
+
+		bindFullTextFilterDef( defAnn );
+	}
+
+	private void bindFullTextFilterDef(FullTextFilterDef defAnn) {
+		FilterDef filterDef = new FilterDef( defAnn );
+		final Map<String, FilterDef> filterDefinition = factoryState.getFilterDefinitions();
+		if ( filterDef.getImpl().equals( ShardSensitiveOnlyFilter.class ) ) {
+			//this is a placeholder don't process regularly
+			filterDefinition.put( defAnn.name(), filterDef );
+			return;
+		}
+		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()
+					);
+				}
+				ReflectionHelper.setAccessible( method );
+				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()
+					);
+				}
+				ReflectionHelper.setAccessible( method );
+				filterDef.setKeyMethod( method );
+			}
+
+			String name = method.getName();
+			if ( name.startsWith( "set" ) && method.getParameterTypes().length == 1 ) {
+				filterDef.addSetter( Introspector.decapitalize( name.substring( 3 ) ), method );
+			}
+		}
+		filterDefinition.put( defAnn.name(), filterDef );
+	}
+
+	private void initProgrammaticAnalyzers(ConfigContext context, ReflectionManager reflectionManager) {
+		final Map defaults = reflectionManager.getDefaults();
+
+		if ( defaults != null ) {
+			AnalyzerDef[] defs = ( AnalyzerDef[] ) defaults.get( AnalyzerDefs.class );
+			if ( defs != null ) {
+				for ( AnalyzerDef def : defs ) {
+					context.addAnalyzerDef( def );
+				}
+			}
+		}
+	}
+
+	private void initProgrammaticallyDefinedFilterDef(ReflectionManager reflectionManager) {
+		@SuppressWarnings("unchecked") Map defaults = reflectionManager.getDefaults();
+		FullTextFilterDef[] filterDefs = ( FullTextFilterDef[] ) defaults.get( FullTextFilterDefs.class );
+		if ( filterDefs != null && filterDefs.length != 0 ) {
+			final Map<String, FilterDef> filterDefinitions = factoryState.getFilterDefinitions();
+			for ( FullTextFilterDef defAnn : filterDefs ) {
+				if ( filterDefinitions.containsKey( defAnn.name() ) ) {
+					throw new SearchException( "Multiple definition of @FullTextFilterDef.name=" + defAnn.name() );
+				}
+				bindFullTextFilterDef( defAnn );
+			}
+		}
+	}
+
+	private static ErrorHandler createErrorHandler(Properties configuration) {
+		String errorHandlerClassName = configuration.getProperty( Environment.ERROR_HANDLER );
+		if ( StringHelper.isEmpty( errorHandlerClassName ) ) {
+			return new LogErrorHandler();
+		}
+		else if ( errorHandlerClassName.trim().equals( "log" ) ) {
+			return new LogErrorHandler();
+		}
+		else {
+			return ClassLoaderHelper.instanceFromName(
+					ErrorHandler.class, errorHandlerClassName,
+					ImmutableSearchFactory.class, "Error Handler"
+			);
+		}
+	}
+
+	private ReflectionManager getReflectionManager(SearchConfiguration cfg) {
+		ReflectionManager reflectionManager = cfg.getReflectionManager();
+		return geReflectionManager( reflectionManager );
+	}
+
+	private ReflectionManager geReflectionManager(ReflectionManager reflectionManager) {
+		if ( reflectionManager == null ) {
+			reflectionManager = new JavaReflectionManager();
+		}
+		return reflectionManager;
+	}
+
+	private static String defineIndexingStrategy(SearchConfiguration cfg) {
+		String indexingStrategy = cfg.getProperties().getProperty( Environment.INDEXING_STRATEGY, "event" );
+		if ( !( "event".equals( indexingStrategy ) || "manual".equals( indexingStrategy ) ) ) {
+			throw new SearchException( Environment.INDEXING_STRATEGY + " unknown: " + indexingStrategy );
+		}
+		return indexingStrategy;
+	}
+
+	/**
+	 * Implementation of the Hibernate Search SPI WritableBuildContext and WorkerBuildContext
+	 * The data is provided by the SearchFactoryState object associated to SearchFactoryBuilder.
+	 */
+	private class BuildContext implements WritableBuildContext, WorkerBuildContext {
+		private final SearchFactoryState factoryState = SearchFactoryBuilder.this.factoryState;
+
+		public void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy optimizerStrategy) {
+			final Map<DirectoryProvider<?>, DirectoryProviderData> dirProviderData = factoryState.getDirectoryProviderData();
+			DirectoryProviderData data = dirProviderData.get( provider );
+			if ( data == null ) {
+				data = new DirectoryProviderData();
+				dirProviderData.put( provider, data );
+			}
+			data.setOptimizerStrategy( optimizerStrategy );
+		}
+
+		public void addIndexingParameters(DirectoryProvider<?> provider, LuceneIndexingParameters indexingParams) {
+			factoryState.getDirectoryProviderIndexingParams().put( provider, indexingParams );
+		}
+
+		public void addClassToDirectoryProvider(Class<?> entity, DirectoryProvider<?> directoryProvider, boolean exclusiveIndexUsage) {
+			final Map<DirectoryProvider<?>, DirectoryProviderData> dirProviderData = factoryState.getDirectoryProviderData();
+			DirectoryProviderData data = dirProviderData.get( directoryProvider );
+			if ( data == null ) {
+				data = new DirectoryProviderData();
+				dirProviderData.put( directoryProvider, data );
+			}
+			data.getClasses().add( entity );
+			data.setExclusiveIndexUsage( exclusiveIndexUsage );
+		}
+
+		public SearchFactoryImplementor getUninitializedSearchFactory() {
+			return rootFactory;
+		}
+
+		public String getIndexingStrategy() {
+			return factoryState.getIndexingStrategy();
+		}
+
+		public Set<DirectoryProvider<?>> getDirectoryProviders() {
+			return factoryState.getDirectoryProviderData().keySet();
+		}
+
+		public void setBackendQueueProcessorFactory(BackendQueueProcessorFactory backendQueueProcessorFactory) {
+			factoryState.setBackendQueueProcessorFactory( backendQueueProcessorFactory );
+		}
+
+		public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider) {
+			return factoryState.getDirectoryProviderData().get( provider ).getOptimizerStrategy();
+		}
+
+		public Set<Class<?>> getClassesInDirectoryProvider(DirectoryProvider<?> directoryProvider) {
+			return Collections.unmodifiableSet( factoryState.getDirectoryProviderData().get( directoryProvider ).getClasses() );
+		}
+
+		public LuceneIndexingParameters getIndexingParameters(DirectoryProvider<?> provider) {
+			return factoryState.getDirectoryProviderIndexingParams().get( provider );
+		}
+
+		public ReentrantLock getDirectoryProviderLock(DirectoryProvider<?> dp) {
+			return factoryState.getDirectoryProviderData().get( dp ).getDirLock();
+		}
+
+		public Similarity getSimilarity(DirectoryProvider<?> provider) {
+			Similarity similarity = factoryState.getDirectoryProviderData().get( provider ).getSimilarity();
+			if ( similarity == null ) {
+				throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
+			}
+			return similarity;
+		}
+
+		public boolean isExclusiveIndexUsageEnabled(DirectoryProvider<?> provider) {
+			return factoryState.getDirectoryProviderData().get( provider ).isExclusiveIndexUsage();
+		}
+
+		public ErrorHandler getErrorHandler() {
+			return factoryState.getErrorHandler();
+		}
+
+		@SuppressWarnings("unchecked")
+		public <T> DocumentBuilderIndexedEntity<T> getDocumentBuilderIndexedEntity(Class<T> entityType) {
+			return ( DocumentBuilderIndexedEntity<T> ) factoryState.getDocumentBuildersIndexedEntities().get( entityType );
+		}
+
+	}
+}

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/configuration/mutablefactory/MutableFactoryTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/configuration/mutablefactory/MutableFactoryTest.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/configuration/mutablefactory/MutableFactoryTest.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -33,6 +33,7 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TopDocs;
+import org.hibernate.search.spi.SearchFactoryBuilder;
 import org.slf4j.Logger;
 
 import org.hibernate.annotations.common.util.ReflectHelper;
@@ -41,7 +42,6 @@
 import org.hibernate.search.batchindexing.Executors;
 import org.hibernate.search.engine.SearchFactoryImplementor;
 import org.hibernate.search.impl.MutableSearchFactory;
-import org.hibernate.search.impl.SearchFactoryBuilder;
 import org.hibernate.search.spi.SearchFactoryIntegrator;
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.store.FSDirectoryProvider;

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/id/providedId/ProvidedIdTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/id/providedId/ProvidedIdTest.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/id/providedId/ProvidedIdTest.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -34,7 +34,7 @@
 import org.hibernate.search.backend.Work;
 import org.hibernate.search.backend.WorkType;
 import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.impl.SearchFactoryBuilder;
+import org.hibernate.search.spi.SearchFactoryBuilder;
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.store.RAMDirectoryProvider;
 import org.hibernate.search.test.SearchTestCase;

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/MutableSearchFactoryAndJMXTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/MutableSearchFactoryAndJMXTest.java	2010-10-14 17:30:18 UTC (rev 20833)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/MutableSearchFactoryAndJMXTest.java	2010-10-14 17:31:27 UTC (rev 20834)
@@ -28,7 +28,7 @@
 import junit.framework.TestCase;
 
 import org.hibernate.search.Environment;
-import org.hibernate.search.impl.SearchFactoryBuilder;
+import org.hibernate.search.spi.SearchFactoryBuilder;
 import org.hibernate.search.store.RAMDirectoryProvider;
 import org.hibernate.search.test.SearchTestCase;
 import org.hibernate.search.test.util.ManualConfiguration;



More information about the hibernate-commits mailing list