org.hibernate.search.engine.common.impl.SearchIntegrationBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-search-engine Show documentation
Show all versions of hibernate-search-engine Show documentation
Hibernate Search engine, always required
The newest version!
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.engine.common.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.EngineSettings;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.EngineSpiSettings;
import org.hibernate.search.engine.common.resources.impl.EngineThreads;
import org.hibernate.search.engine.common.spi.SearchIntegration;
import org.hibernate.search.engine.common.spi.SearchIntegrationEnvironment;
import org.hibernate.search.engine.common.spi.SearchIntegrationPartialBuildState;
import org.hibernate.search.engine.common.timing.impl.DefaultTimingSource;
import org.hibernate.search.engine.common.timing.spi.TimingSource;
import org.hibernate.search.engine.environment.bean.BeanHolder;
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.environment.bean.BeanResolver;
import org.hibernate.search.engine.environment.thread.impl.ThreadPoolProviderImpl;
import org.hibernate.search.engine.environment.thread.spi.ThreadProvider;
import org.hibernate.search.engine.mapper.mapping.building.spi.BackendsInfo;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappedIndexManagerFactory;
import org.hibernate.search.engine.mapper.mapping.building.spi.Mapper;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingAbortedException;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingBuildContext;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingConfigurationCollector;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingInitiator;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingKey;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingPartialBuildState;
import org.hibernate.search.engine.mapper.model.spi.MappableTypeModel;
import org.hibernate.search.engine.mapper.model.spi.TypeMetadataContributorProvider;
import org.hibernate.search.engine.mapper.model.spi.TypeMetadataDiscoverer;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.reporting.impl.EngineEventContextMessages;
import org.hibernate.search.engine.reporting.impl.FailSafeFailureHandlerWrapper;
import org.hibernate.search.engine.reporting.spi.RootFailureCollector;
import org.hibernate.search.util.common.AssertionFailure;
import org.hibernate.search.util.common.SearchException;
import org.hibernate.search.util.common.impl.SuppressingCloser;
public class SearchIntegrationBuilder implements SearchIntegration.Builder {
private static final ConfigurationProperty> BACKGROUND_FAILURE_HANDLER =
ConfigurationProperty.forKey( EngineSettings.Radicals.BACKGROUND_FAILURE_HANDLER )
.asBeanReference( FailureHandler.class )
.withDefault( EngineSettings.Defaults.BACKGROUND_FAILURE_HANDLER )
.build();
private static final ConfigurationProperty> THREAD_PROVIDER =
ConfigurationProperty.forKey( EngineSpiSettings.Radicals.THREAD_PROVIDER )
.asBeanReference( ThreadProvider.class )
.withDefault( EngineSpiSettings.Defaults.THREAD_PROVIDER )
.build();
private final SearchIntegrationEnvironment environment;
private final Optional previousIntegration;
private final Map, MappingInitiator, ?>> mappingInitiators = new LinkedHashMap<>();
private boolean frozen = false;
public SearchIntegrationBuilder(SearchIntegrationEnvironment environment,
Optional previousIntegration) {
this.environment = environment;
this.previousIntegration = previousIntegration;
environment.propertyChecker().beforeBoot();
}
@Override
public SearchIntegration.Builder addMappingInitiator(
MappingKey mappingKey, MappingInitiator, PBM> initiator) {
if ( frozen ) {
throw new AssertionFailure(
"Attempt to add a mapping initiator"
+ " after Hibernate Search has started to build the mappings."
);
}
MappingInitiator, ?> existing = mappingInitiators.putIfAbsent( mappingKey, initiator );
if ( existing != null ) {
throw new AssertionFailure(
"Mapping key '" + mappingKey + "' has multiple initiators: '"
+ existing + "', '" + initiator + "'."
);
}
return this;
}
@Override
public SearchIntegrationPartialBuildState prepareBuild() {
ConfigurationPropertySource propertySource = environment.propertySource();
BeanResolver beanResolver = environment.beanResolver();
BeanHolder extends FailureHandler> failureHandlerHolder = null;
BeanHolder extends ThreadProvider> threadProviderHolder = null;
IndexManagerBuildingStateHolder indexManagerBuildingStateHolder = null;
// Use a LinkedHashMap for deterministic iteration
List> mappingBuildingStates = new ArrayList<>();
Map, MappingPartialBuildState> partiallyBuiltMappings = new HashMap<>();
RootFailureCollector failureCollector = new RootFailureCollector( EngineEventContextMessages.INSTANCE.bootstrap() );
boolean checkingRootFailures = false;
EngineThreads engineThreads = null;
TimingSource timingSource = null;
try {
frozen = true;
failureHandlerHolder = BACKGROUND_FAILURE_HANDLER.getAndTransform( propertySource, beanResolver::resolve );
// Wrap the failure handler to prevent it from throwing exceptions
failureHandlerHolder = BeanHolder.of( new FailSafeFailureHandlerWrapper( failureHandlerHolder.get() ) )
.withDependencyAutoClosing( failureHandlerHolder );
FailureHandler failureHandler = failureHandlerHolder.get();
threadProviderHolder = THREAD_PROVIDER.getAndTransform( propertySource, beanResolver::resolve );
ThreadPoolProviderImpl threadPoolProvider = new ThreadPoolProviderImpl( threadProviderHolder );
engineThreads = new EngineThreads( threadPoolProvider );
timingSource = new DefaultTimingSource( engineThreads );
RootBuildContext rootBuildContext = new RootBuildContext(
propertySource,
environment.classResolver(), environment.resourceResolver(), beanResolver,
failureCollector, threadPoolProvider, failureHandler,
engineThreads, timingSource
);
indexManagerBuildingStateHolder =
new IndexManagerBuildingStateHolder( beanResolver, propertySource, rootBuildContext );
// Step #1: collect configuration for all mappings
for ( Map.Entry, MappingInitiator, ?>> entry : mappingInitiators.entrySet() ) {
// We know the key and initiator have compatible types, see how they are put into the map
@SuppressWarnings({ "rawtypes", "unchecked" })
MappingBuildingState, ?> mappingBuildingState = new MappingBuildingState<>(
rootBuildContext,
(MappingKey) entry.getKey(), entry.getValue()
);
mappingBuildingStates.add( mappingBuildingState );
mappingBuildingState.collect();
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
// Step #2: create mappers
for ( MappingBuildingState, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.createMapper();
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
// Step #3: determine indexed types and the necessary backends
BackendsInfo backendsInfo = new BackendsInfo();
for ( MappingBuildingState, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.determineIndexedTypes( backendsInfo );
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
// Step #4: create backends that will be necessary for mappers
indexManagerBuildingStateHolder.createBackends( backendsInfo );
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
// Step #5: map indexed types and create the corresponding index managers
MappedIndexManagerFactory mappedIndexManagerFactory =
new MappedIndexManagerFactoryImpl( indexManagerBuildingStateHolder );
for ( MappingBuildingState, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.mapIndexedTypes( mappedIndexManagerFactory );
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
// Step #6: create mappings
for ( MappingBuildingState, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.partiallyBuildAndAddTo( partiallyBuiltMappings );
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;
return new SearchIntegrationPartialBuildStateImpl(
environment.beanProvider(), beanResolver,
failureHandlerHolder,
threadPoolProvider,
partiallyBuiltMappings,
indexManagerBuildingStateHolder.getBackendNonStartedStates(),
indexManagerBuildingStateHolder.getIndexManagersNonStartedStates(),
environment.propertyChecker(),
engineThreads, timingSource, previousIntegration
);
}
catch (RuntimeException e) {
RuntimeException rethrownException;
if ( checkingRootFailures ) {
// The exception was thrown by one of the failure checks above. No need for an additional check.
rethrownException = e;
}
else {
/*
* The exception was thrown by something other than the failure checks above
* (a mapper, a backend, ...).
* We should check that no failure was collected before.
*/
try {
failureCollector.checkNoFailure();
// No other failure, just rethrow the exception.
rethrownException = e;
}
catch (SearchException e2) {
/*
* At least one failure was collected, most likely before "e" was even thrown.
* Let's throw "e2" (which mentions prior failures), only mentioning "e" as a suppressed exception.
*/
rethrownException = e2;
rethrownException.addSuppressed( e );
}
}
SuppressingCloser closer = new SuppressingCloser( rethrownException );
// Release the failure handler before aborting
closer.push( failureHandlerHolder );
// Close the mappers and mappings created so far before aborting
closer.pushAll( MappingPartialBuildState::closeOnFailure, partiallyBuiltMappings.values() );
closer.pushAll( MappingBuildingState::closeOnFailure, mappingBuildingStates );
// Close the resources contained in the index manager building state before aborting
closer.pushAll( holder -> holder.closeOnFailure( closer ), indexManagerBuildingStateHolder );
// Close environment resources before aborting
closer.pushAll( BeanHolder::close, threadProviderHolder );
closer.pushAll( SearchIntegrationEnvironment::close, environment );
closer.push( EngineThreads::onStop, engineThreads );
closer.push( TimingSource::stop, timingSource );
throw rethrownException;
}
}
private static class MappingBuildingState {
private final MappingBuildContext buildContext;
private final MappingKey mappingKey;
private final MappingInitiator mappingInitiator;
private TypeMetadataContributorProvider metadataContributorProvider;
private Mapper mapper; // Initially null, set in createMapper()
MappingBuildingState(RootBuildContext rootBuildContext,
MappingKey mappingKey, MappingInitiator mappingInitiator) {
this.mappingKey = mappingKey;
this.buildContext = new MappingBuildContextImpl( rootBuildContext, mappingKey );
this.mappingInitiator = mappingInitiator;
}
void collect() {
TypeMetadataContributorProvider.Builder builder = TypeMetadataContributorProvider.builder();
mappingInitiator.configure( buildContext, new MappingConfigurationCollectorImpl( builder ) );
metadataContributorProvider = builder.build();
}
void createMapper() {
mapper = mappingInitiator.createMapper( buildContext, metadataContributorProvider );
}
void determineIndexedTypes(BackendsInfo backendsInfo) {
mapper.prepareMappedTypes( backendsInfo );
}
void mapIndexedTypes(MappedIndexManagerFactory indexManagerFactory) {
mapper.mapTypes( indexManagerFactory );
}
void partiallyBuildAndAddTo(Map, MappingPartialBuildState> mappings) {
try {
PBM partiallyBuiltMapping = mapper.prepareBuild();
mappings.put( mappingKey, partiallyBuiltMapping );
}
catch (MappingAbortedException e) {
e.collectSilentlyAndCheck( buildContext.failureCollector() );
}
}
public void closeOnFailure() {
if ( mapper != null ) {
mapper.closeOnFailure();
}
}
private class MappingConfigurationCollectorImpl implements MappingConfigurationCollector {
private final TypeMetadataContributorProvider.Builder builder;
private MappingConfigurationCollectorImpl(TypeMetadataContributorProvider.Builder builder) {
this.builder = builder;
}
@Override
public void collectContributor(MappableTypeModel typeModel, C contributor) {
builder.contributor( typeModel, contributor );
}
@Override
public void collectDiscoverer(TypeMetadataDiscoverer metadataDiscoverer) {
builder.discoverer( metadataDiscoverer );
}
}
}
}