All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.search.engine.impl.ConfigContext Maven / Gradle / Ivy

There is a newer version: 5.11.12.Final
Show newest version
/*
 * Hibernate Search, full-text search for your domain model
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.search.engine.impl;

import java.beans.Introspector;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.annotations.ClassBridge;
import org.hibernate.search.annotations.Factory;
import org.hibernate.search.annotations.FullTextFilterDef;
import org.hibernate.search.annotations.Key;
import org.hibernate.search.annotations.NormalizerDef;
import org.hibernate.search.bridge.FieldBridge;
import org.hibernate.search.cfg.EntityDescriptor;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.cfg.SearchMapping;
import org.hibernate.search.cfg.spi.SearchConfiguration;
import org.hibernate.search.engine.integration.impl.SearchIntegration;
import org.hibernate.search.engine.service.spi.ServiceManager;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.filter.ShardSensitiveOnlyFilter;
import org.hibernate.search.indexes.impl.IndexManagerHolder;
import org.hibernate.search.indexes.spi.IndexManagerType;
import org.hibernate.search.indexes.spi.LuceneEmbeddedIndexManagerType;
import org.hibernate.search.spi.BuildContext;
import org.hibernate.search.util.StringHelper;
import org.hibernate.search.util.impl.ReflectionHelper;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.util.logging.impl.LoggerFactory;
import java.lang.invoke.MethodHandles;


/**
 * Provides access to some default configuration settings (eg is JPA present, what is the default null token)
 * and holds mapping-scoped configuration (such as analyzer definitions).
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
public final class ConfigContext {

	private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );

	/**
	 * The default token for indexing null values. See {@link org.hibernate.search.annotations.Field#indexNullAs()}
	 */
	private static final String DEFAULT_NULL_INDEX_TOKEN = "_null_";

	private final MappingDefinitionRegistry analyzerDefinitionRegistry =
			new MappingDefinitionRegistry<>( Function.identity(), LOG::analyzerDefinitionNamingConflict );

	private final MappingDefinitionRegistry normalizerDefinitionRegistry =
			new MappingDefinitionRegistry<>( this::interpretNormalizerDef, LOG::normalizerDefinitionNamingConflict );

	private final MappingDefinitionRegistry fullTextFilterDefinitionRegistry =
			new MappingDefinitionRegistry<>( this::interpretFullTextFilterDef, LOG::fullTextFilterDefinitionNamingConflict );

	private final Map indexManagerTypeConfigContexts = new HashMap<>();

	private final boolean jpaPresent;
	private final String nullToken;
	private final boolean implicitProvidedId;
	private final SearchMapping searchMapping;
	private final ServiceManager serviceManager;
	private final SearchConfiguration searchConfiguration;

	public ConfigContext(SearchConfiguration searchConfiguration, BuildContext buildContext) {
		this( searchConfiguration, buildContext, null, null );
	}

	public ConfigContext(SearchConfiguration searchConfiguration, BuildContext buildContext, SearchMapping searchMapping,
			Map previousSearchIntegrations) {
		this.serviceManager = buildContext.getServiceManager();
		this.jpaPresent = searchConfiguration.isJPAAnnotationsProcessingEnabled();
		this.nullToken = initNullToken( searchConfiguration );
		this.implicitProvidedId = searchConfiguration.isIdProvidedImplicit();
		this.searchMapping = searchMapping;
		this.searchConfiguration = searchConfiguration;
		if ( previousSearchIntegrations != null ) {
			for ( Map.Entry entry : previousSearchIntegrations.entrySet() ) {
				IndexManagerType type = entry.getKey();
				SearchIntegration integrationState = entry.getValue();
				SearchIntegrationConfigContext context = new SearchIntegrationConfigContext(
						type, serviceManager, searchConfiguration, integrationState );
				indexManagerTypeConfigContexts.put( type, context );
			}
		}
	}

	public ServiceManager getServiceManager() {
		return serviceManager;
	}

	public MappingDefinitionRegistry getAnalyzerDefinitionRegistry() {
		return analyzerDefinitionRegistry;
	}

	public MappingDefinitionRegistry getNormalizerDefinitionRegistry() {
		return normalizerDefinitionRegistry;
	}

	public MappingDefinitionRegistry getFullTextFilterDefinitionRegistry() {
		return fullTextFilterDefinitionRegistry;
	}

	public SearchIntegrationConfigContext forType(IndexManagerType type) {
		SearchIntegrationConfigContext context = indexManagerTypeConfigContexts.get( type );
		if ( context == null ) {
			context = new SearchIntegrationConfigContext( type, serviceManager, searchConfiguration );
			indexManagerTypeConfigContexts.put( type, context );
		}
		return context;
	}

	private String initNullToken(SearchConfiguration cfg) {
		String defaultNullIndexToken = cfg.getProperty( Environment.DEFAULT_NULL_TOKEN );
		if ( StringHelper.isEmpty( defaultNullIndexToken ) ) {
			defaultNullIndexToken = DEFAULT_NULL_INDEX_TOKEN;
		}
		return defaultNullIndexToken;
	}

	public String getDefaultNullToken() {
		return nullToken;
	}

	/**
	 * Check that a given {@link NormalizerDef} is "well-formed",
	 * i.e. that it defines at least one char filter or token filter.
	 *
	 * @param normalizerDef The normalizer def to check
	 * @return The same normalizer def
	 */
	private NormalizerDef interpretNormalizerDef(NormalizerDef normalizerDef) {
		if ( normalizerDef.charFilters().length == 0 && normalizerDef.filters().length == 0 ) {
			throw LOG.invalidEmptyNormalizerDefinition( normalizerDef.name() );
		}

		return normalizerDef;
	}

	private FilterDef interpretFullTextFilterDef(FullTextFilterDef defAnn) {
		FilterDef filterDef = new FilterDef( defAnn );
		if ( filterDef.getImpl().equals( ShardSensitiveOnlyFilter.class ) ) {
			//this is a placeholder don't process regularly
			return filterDef;
		}
		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 );
			}
		}
		return filterDef;
	}

	/**
	 * Initialize integrations for all discovered index manager types.
	 *
	 * In particular, initialize the named analyzer references created throughout the mapping creation
	 * with the analyzer definitions collected throughout the mapping creation.
	 * 

* Analyzer definitions and references are handled simultaneously during the mapping * creation, so it's actually possible that, while creating the mapping, we encounter * references to analyzer which haven't been defined yet. *

* To work around this issue, we do not resolve references immediately, but instead * create "dangling" references whose initialization will be delayed to the end of * the mapping (see {@link SearchIntegrationConfigContext#getAnalyzerRegistry()}). *

* This method executes the final initialization, resolving dangling references. * * @param indexesFactory The index manager holder, giving access to the relevant index manager types. * @return The initialized (and immutable) search integrations. */ public Map initIntegrations(IndexManagerHolder indexesFactory) { Map mappingAnalyzerDefs = analyzerDefinitionRegistry.getAll(); Map mappingNormalizerDefs = normalizerDefinitionRegistry.getAll(); /* * For analyzers/normalizers defined in the mapping, but not referenced in this mapping, * we assume these are Lucene analyzer definitions that will be used * when querying. * Thus we create references to these definitions, so that the references * are initialized below, making the analyzers available at runtime. */ for ( String name : mappingAnalyzerDefs.keySet() ) { if ( !hasAnalyzerReference( name ) ) { MutableAnalyzerRegistry registry = forType( LuceneEmbeddedIndexManagerType.INSTANCE ) .getAnalyzerRegistry(); registry.getOrCreateAnalyzerReference( name ); } } for ( String name : mappingNormalizerDefs.keySet() ) { if ( !hasNormalizerReference( name ) ) { MutableNormalizerRegistry registry = forType( LuceneEmbeddedIndexManagerType.INSTANCE ) .getNormalizerRegistry(); registry.getOrCreateNamedNormalizerReference( name ); } } // Put the types in a Set to avoid duplicates Set indexManagerTypes = new HashSet<>(); indexManagerTypes.addAll( indexesFactory.getIndexManagerTypes() ); // Make sure to initialize every registry, even those that are not used by index managers (see the loop above) indexManagerTypes.addAll( indexManagerTypeConfigContexts.keySet() ); final Map immutableSearchIntegrations = new HashMap<>( indexManagerTypes.size() ); for ( IndexManagerType indexManagerType : indexManagerTypes ) { ImmutableSearchIntegration searchIntegration = forType( indexManagerType ) .initialize( mappingAnalyzerDefs, mappingNormalizerDefs ); immutableSearchIntegrations.put( indexManagerType, searchIntegration ); } return immutableSearchIntegrations; } private boolean hasAnalyzerReference(String name) { for ( SearchIntegrationConfigContext context : indexManagerTypeConfigContexts.values() ) { MutableAnalyzerRegistry registry = context.getAnalyzerRegistry(); if ( registry.getNamedAnalyzerReferences().containsKey( name ) ) { return true; } } return false; } private boolean hasNormalizerReference(String name) { for ( SearchIntegrationConfigContext context : indexManagerTypeConfigContexts.values() ) { MutableNormalizerRegistry registry = context.getNormalizerRegistry(); if ( registry.getNamedNormalizerReferences().containsKey( name ) ) { return true; } } return false; } public Map initFilters() { return fullTextFilterDefinitionRegistry.getAll(); } public boolean isJpaPresent() { return jpaPresent; } /** * @return true if we have to assume entities are annotated with @ProvidedId implicitly */ public boolean isProvidedIdImplicit() { return implicitProvidedId; } /** * Returns class bridge instances configured via the programmatic API, if any. The returned map's values are * {@code @ClassBridge} annotations representing the corresponding analyzer etc. configuration. * * @param type the type for which to return the configured class bridge instances * * @return a map with class bridge instances and their configuration; May be empty but never {@code null} */ public Map getClassBridgeInstances(Class type) { Map classBridgeInstances = null; if ( searchMapping != null ) { EntityDescriptor entityDescriptor = searchMapping.getEntityDescriptor( type ); if ( entityDescriptor != null ) { classBridgeInstances = entityDescriptor.getClassBridgeConfigurations(); } } return classBridgeInstances != null ? classBridgeInstances : Collections.emptyMap(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy