Skip to content

Commit

Permalink
HSEARCH-3523 Split the mapping integration in two phases, to be able …
Browse files Browse the repository at this point in the history
…to pass the SessionFactory later to the ORM mapper
  • Loading branch information
yrodiere committed Mar 21, 2019
1 parent ff87b28 commit 6e16fc5
Show file tree
Hide file tree
Showing 32 changed files with 395 additions and 116 deletions.
Expand Up @@ -14,8 +14,7 @@ class MappingBuildContextImpl extends DelegatingBuildContext implements MappingB

private final ContextualFailureCollector failureCollector;


MappingBuildContextImpl(RootBuildContext delegate, MappingKey<?> mappingKey) {
MappingBuildContextImpl(RootBuildContext delegate, MappingKey<?, ?> mappingKey) {
super( delegate );
failureCollector = delegate.getFailureCollector().withContext( mappingKey );
}
Expand Down
Expand Up @@ -23,7 +23,7 @@

import org.hibernate.search.engine.backend.index.spi.IndexManagerImplementor;
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.common.spi.SearchIntegration;
import org.hibernate.search.engine.common.spi.SearchIntegrationPartialBuildState;
import org.hibernate.search.engine.common.spi.SearchIntegrationBuilder;
import org.hibernate.search.engine.environment.bean.BeanProvider;
import org.hibernate.search.engine.environment.bean.impl.ConfiguredBeanProvider;
Expand All @@ -46,8 +46,8 @@
import org.hibernate.search.engine.mapper.mapping.building.spi.TypeMetadataContributorProvider;
import org.hibernate.search.engine.mapper.mapping.building.spi.TypeMetadataDiscoverer;
import org.hibernate.search.engine.mapper.mapping.spi.MappingBuildContext;
import org.hibernate.search.engine.mapper.mapping.spi.MappingImplementor;
import org.hibernate.search.engine.mapper.mapping.spi.MappingKey;
import org.hibernate.search.engine.mapper.mapping.spi.MappingPartialBuildState;
import org.hibernate.search.engine.mapper.model.spi.MappableTypeModel;
import org.hibernate.search.util.common.AssertionFailure;
import org.hibernate.search.util.common.SearchException;
Expand All @@ -62,7 +62,7 @@ public class SearchIntegrationBuilderImpl implements SearchIntegrationBuilder {

private final ConfigurationPropertySource mainPropertySource;
private final Map<String, Object> overriddenProperties = new LinkedHashMap<>();
private final Map<MappingKey<?>, MappingInitiator<?, ?>> mappingInitiators = new LinkedHashMap<>();
private final Map<MappingKey<?, ?>, MappingInitiator<?, ?>> mappingInitiators = new LinkedHashMap<>();

private ClassResolver classResolver;
private ResourceResolver resourceResolver;
Expand Down Expand Up @@ -98,8 +98,8 @@ public SearchIntegrationBuilder setProperty(String name, Object value) {
}

@Override
public <M> SearchIntegrationBuilder addMappingInitiator(MappingKey<M> mappingKey,
MappingInitiator<?, M> initiator) {
public <PBM extends MappingPartialBuildState> SearchIntegrationBuilder addMappingInitiator(
MappingKey<PBM, ?> mappingKey, MappingInitiator<?, PBM> initiator) {
if ( frozen ) {
throw new AssertionFailure(
"Attempt to add a mapping initiator"
Expand All @@ -121,11 +121,11 @@ public <M> SearchIntegrationBuilder addMappingInitiator(MappingKey<M> mappingKey
}

@Override
public SearchIntegration build() {
public SearchIntegrationPartialBuildState prepareBuild() {
IndexManagerBuildingStateHolder indexManagerBuildingStateHolder = null;
// Use a LinkedHashMap for deterministic iteration
List<MappingBuildingState<?, ?>> mappingBuildingStates = new ArrayList<>();
Map<MappingKey<?>, MappingImplementor<?>> mappings = new HashMap<>();
Map<MappingKey<?, ?>, MappingPartialBuildState> partiallyBuiltMappings = new HashMap<>();
RootFailureCollector failureCollector = new RootFailureCollector( FAILURE_LIMIT );
boolean checkingRootFailures = false;

Expand Down Expand Up @@ -166,8 +166,8 @@ public SearchIntegration build() {

indexManagerBuildingStateHolder = new IndexManagerBuildingStateHolder( beanProvider, propertySource, rootBuildContext );

// First phase: collect configuration for all mappings
for ( Map.Entry<MappingKey<?>, MappingInitiator<?, ?>> entry : mappingInitiators.entrySet() ) {
// First step: collect configuration for all mappings
for ( Map.Entry<MappingKey<?, ?>, 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<>(
Expand All @@ -181,23 +181,23 @@ public SearchIntegration build() {
failureCollector.checkNoFailure();
checkingRootFailures = false;

// Second phase: create mappers and their backing index managers
// Second step: create mappers and their backing index managers
for ( MappingBuildingState<?, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.createMapper( indexManagerBuildingStateHolder );
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;

// Third phase: create mappings
// Third step: create mappings
for ( MappingBuildingState<?, ?> mappingBuildingState : mappingBuildingStates ) {
mappingBuildingState.createAndAddMapping( mappings );
mappingBuildingState.partiallyBuildAndAddTo( partiallyBuiltMappings );
}
checkingRootFailures = true;
failureCollector.checkNoFailure();
checkingRootFailures = false;

// Fourth phase: start indexes
// Fourth step: start indexes
for ( Map.Entry<String, IndexManagerImplementor<?>> entry :
indexManagerBuildingStateHolder.getIndexManagersByName().entrySet() ) {
String indexName = entry.getKey();
Expand All @@ -217,9 +217,9 @@ public SearchIntegration build() {
failureCollector.checkNoFailure();
checkingRootFailures = false;

return new SearchIntegrationImpl(
return new SearchIntegrationPartialBuildStateImpl(
beanResolver,
mappings,
partiallyBuiltMappings,
indexManagerBuildingStateHolder.getBackendsByName(),
indexManagerBuildingStateHolder.getIndexManagersByName()
);
Expand Down Expand Up @@ -253,7 +253,7 @@ public SearchIntegration build() {

SuppressingCloser closer = new SuppressingCloser( rethrownException );
// Close the mappers and mappings created so far before aborting
closer.pushAll( MappingImplementor::close, mappings.values() );
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 );
Expand All @@ -264,11 +264,11 @@ public SearchIntegration build() {
}
}

private static class MappingBuildingState<C, M> {
private static class MappingBuildingState<C, PBM extends MappingPartialBuildState> {
private final MappingBuildContext buildContext;

private final MappingKey<M> mappingKey;
private final MappingInitiator<C, M> mappingInitiator;
private final MappingKey<PBM, ?> mappingKey;
private final MappingInitiator<C, PBM> mappingInitiator;

// Use a LinkedHashMap for deterministic iteration
private final Map<MappableTypeModel, TypeMappingContribution<C>> contributionByType = new LinkedHashMap<>();
Expand All @@ -277,10 +277,10 @@ private static class MappingBuildingState<C, M> {

private final Set<MappableTypeModel> typesSubmittedToDiscoverers = new HashSet<>();

private Mapper<M> mapper; // Initially null, set in createMapper()
private Mapper<PBM> mapper; // Initially null, set in createMapper()

MappingBuildingState(RootBuildContext rootBuildContext,
MappingKey<M> mappingKey, MappingInitiator<C, M> mappingInitiator) {
MappingKey<PBM, ?> mappingKey, MappingInitiator<C, PBM> mappingInitiator) {
this.mappingKey = mappingKey;
this.buildContext = new MappingBuildContextImpl( rootBuildContext, mappingKey );
this.mappingInitiator = mappingInitiator;
Expand Down Expand Up @@ -321,10 +321,10 @@ void createMapper(IndexManagerBuildingStateHolder indexManagerBuildingStateHolde
}
}

void createAndAddMapping(Map<MappingKey<?>, MappingImplementor<?>> mappings) {
void partiallyBuildAndAddTo(Map<MappingKey<?, ?>, MappingPartialBuildState> mappings) {
try {
MappingImplementor<M> mapping = mapper.build();
mappings.put( mappingKey, mapping );
PBM partiallyBuiltMapping = mapper.prepareBuild();
mappings.put( mappingKey, partiallyBuiltMapping );
}
catch (MappingAbortedException e) {
ContextualFailureCollector failureCollector = buildContext.getFailureCollector();
Expand Down
Expand Up @@ -27,12 +27,12 @@ public class SearchIntegrationImpl implements SearchIntegration {

private final BeanResolver beanResolver;

private final Map<MappingKey<?>, MappingImplementor<?>> mappings;
private final Map<MappingKey<?, ?>, MappingImplementor<?>> mappings;
private final Map<String, BackendImplementor<?>> backends;
private final Map<String, IndexManagerImplementor<?>> indexManagers;

SearchIntegrationImpl(BeanResolver beanResolver,
Map<MappingKey<?>, MappingImplementor<?>> mappings,
Map<MappingKey<?, ?>, MappingImplementor<?>> mappings,
Map<String, BackendImplementor<?>> backends,
Map<String, IndexManagerImplementor<?>> indexManagers) {
this.beanResolver = beanResolver;
Expand All @@ -42,7 +42,7 @@ public class SearchIntegrationImpl implements SearchIntegration {
}

@Override
public <M> M getMapping(MappingKey<M> mappingKey) {
public <M> M getMapping(MappingKey<?, M> mappingKey) {
// See SearchIntegrationBuilderImpl: we are sure that, if there is a mapping, it implements MappingImplementor<M>
@SuppressWarnings("unchecked")
MappingImplementor<M> mappingImplementor = (MappingImplementor<M>) mappings.get( mappingKey );
Expand Down
@@ -0,0 +1,91 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.common.impl;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;

import org.hibernate.search.engine.backend.index.spi.IndexManagerImplementor;
import org.hibernate.search.engine.backend.spi.BackendImplementor;
import org.hibernate.search.engine.common.spi.SearchIntegrationPartialBuildState;
import org.hibernate.search.engine.common.spi.SearchIntegration;
import org.hibernate.search.engine.environment.bean.spi.BeanResolver;
import org.hibernate.search.engine.mapper.mapping.spi.MappingImplementor;
import org.hibernate.search.engine.mapper.mapping.spi.MappingKey;
import org.hibernate.search.engine.mapper.mapping.spi.MappingPartialBuildState;
import org.hibernate.search.util.common.AssertionFailure;
import org.hibernate.search.util.common.impl.Closer;

class SearchIntegrationPartialBuildStateImpl implements SearchIntegrationPartialBuildState {

private final BeanResolver beanResolver;

private final Map<MappingKey<?, ?>, MappingPartialBuildState> partiallyBuiltMappings;
private final Map<MappingKey<?, ?>, MappingImplementor<?>> fullyBuiltMappings = new LinkedHashMap<>();
private final Map<String, BackendImplementor<?>> backends;
private final Map<String, IndexManagerImplementor<?>> indexManagers;

SearchIntegrationPartialBuildStateImpl(
BeanResolver beanResolver,
Map<MappingKey<?, ?>, MappingPartialBuildState> partiallyBuiltMappings,
Map<String, BackendImplementor<?>> backends,
Map<String, IndexManagerImplementor<?>> indexManagers) {
this.beanResolver = beanResolver;
this.partiallyBuiltMappings = partiallyBuiltMappings;
this.backends = backends;
this.indexManagers = indexManagers;
}

@Override
public void closeOnFailure() {
try ( Closer<RuntimeException> closer = new Closer<>() ) {
closer.pushAll( MappingPartialBuildState::closeOnFailure, partiallyBuiltMappings.values() );
closer.pushAll( MappingImplementor::close, fullyBuiltMappings.values() );
closer.pushAll( IndexManagerImplementor::close, indexManagers.values() );
closer.pushAll( BackendImplementor::close, backends.values() );
closer.pushAll( BeanResolver::close, beanResolver );
}
}

@Override
public <PBM, M> M finalizeMapping(MappingKey<PBM, M> mappingKey,
Function<PBM, MappingImplementor<M>> director) {
// We know this cast will work because of how
@SuppressWarnings("unchecked")
PBM partiallyBuiltMapping = (PBM) partiallyBuiltMappings.get( mappingKey );
if ( partiallyBuiltMapping == null ) {
throw new AssertionFailure(
"Some partially built mapping could not be found during bootstrap; there is probably a bug in Hibernate Search."
+ " Key: " + mappingKey
);
}

MappingImplementor<M> mapping = director.apply( partiallyBuiltMapping );
fullyBuiltMappings.put( mappingKey, mapping );
partiallyBuiltMappings.remove( mappingKey );

return mapping.toAPI();
}

@Override
public SearchIntegration finalizeIntegration() {
if ( !partiallyBuiltMappings.isEmpty() ) {
throw new AssertionFailure(
"Some mappings were not fully built; there is probably a bug in Hibernate Search."
+ " Partially built mappings: " + partiallyBuiltMappings
);
}

return new SearchIntegrationImpl(
beanResolver,
fullyBuiltMappings,
backends,
indexManagers
);
}
}
Expand Up @@ -14,7 +14,7 @@

public interface SearchIntegration extends AutoCloseable {

<M> M getMapping(MappingKey<M> mappingKey);
<M> M getMapping(MappingKey<?, M> mappingKey);

Backend getBackend(String backendName);

Expand Down
Expand Up @@ -11,6 +11,7 @@
import org.hibernate.search.engine.environment.classpath.spi.ResourceResolver;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingInitiator;
import org.hibernate.search.engine.mapper.mapping.spi.MappingKey;
import org.hibernate.search.engine.mapper.mapping.spi.MappingPartialBuildState;

/**
* @author Yoann Rodiere
Expand All @@ -25,8 +26,9 @@ public interface SearchIntegrationBuilder {

SearchIntegrationBuilder setProperty(String name, Object value);

<M> SearchIntegrationBuilder addMappingInitiator(MappingKey<M> mappingKey, MappingInitiator<?, M> initiator);
<PBM extends MappingPartialBuildState> SearchIntegrationBuilder addMappingInitiator(
MappingKey<PBM, ?> mappingKey, MappingInitiator<?, PBM> initiator);

SearchIntegration build();
SearchIntegrationPartialBuildState prepareBuild();

}
@@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.common.spi;

import java.util.function.Function;

import org.hibernate.search.engine.mapper.mapping.spi.MappingImplementor;
import org.hibernate.search.engine.mapper.mapping.spi.MappingKey;

public interface SearchIntegrationPartialBuildState {

/**
* Close the resources held by this object (backends, index managers, ...).
* <p>
* To be called in the event of a failure that will prevent {@link #finalizeIntegration()} to be called.
*/
void closeOnFailure();

/**
* Finalize the building of a mapping.
* @param mappingKey The mapping key allowing to retrieve the pre-built mapping.
* @param director The object responsible for turning a pre-built mapping into a fully-built mapping (it may hold some additional context).
* @param <PBM> The type of the partially-built mapping.
* @param <M> The type of the fully-built mapping.
* @return The fully-built mapping.
*/
<PBM, M> M finalizeMapping(MappingKey<PBM, M> mappingKey, Function<PBM, MappingImplementor<M>> director);

/**
* Finalize the building of the integration.
* @return The fully-built integration.
*/
SearchIntegration finalizeIntegration();

}
Expand Up @@ -188,7 +188,7 @@ SearchException relativeFieldNameCannotContainDot(String relativeFieldName,
SearchException multipleIndexMapping(@FormatWith(MappableTypeModelFormatter.class) MappableTypeModel typeModel, String indexName, String otherIndexName);

@Message(id = ID_OFFSET_2 + 32, value = "No mapping registered for mapping key: '%1$s'.")
SearchException noMappingRegistered(@FormatWith(MappingKeyFormatter.class) MappingKey<?> mappingKey);
SearchException noMappingRegistered(@FormatWith(MappingKeyFormatter.class) MappingKey<?, ?> mappingKey);

@Message(id = ID_OFFSET_2 + 33, value = "No backend registered for backend name: '%1$s'.")
SearchException noBackendRegistered(String backendName);
Expand Down
Expand Up @@ -12,7 +12,7 @@ public class MappingKeyFormatter {

private final String formatted;

public MappingKeyFormatter(MappingKey<?> mappingKey) {
public MappingKeyFormatter(MappingKey<?, ?> mappingKey) {
this.formatted = mappingKey.toString();
}

Expand Down

0 comments on commit 6e16fc5

Please sign in to comment.