diff --git a/backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchBackendFactory.java b/backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchBackendFactory.java index a35e0b37bee..003238ada7f 100644 --- a/backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchBackendFactory.java +++ b/backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchBackendFactory.java @@ -38,6 +38,7 @@ import org.hibernate.search.engine.cfg.spi.ConfigurationProperty; import org.hibernate.search.engine.backend.spi.BackendBuildContext; import org.hibernate.search.engine.environment.bean.BeanProvider; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.engine.logging.spi.EventContexts; import org.hibernate.search.util.AssertionFailure; import org.hibernate.search.util.EventContext; @@ -66,6 +67,11 @@ public class ElasticsearchBackendFactory implements BackendFactory { .withDefault( SearchBackendElasticsearchSettings.Defaults.LOG_JSON_PRETTY_PRINTING ) .build(); + private static final ConfigurationProperty> ANALYSIS_CONFIGURER = + ConfigurationProperty.forKey( SearchBackendElasticsearchSettings.ANALYSIS_CONFIGURER ) + .asBeanReference( ElasticsearchAnalysisConfigurer.class ) + .build(); + @Override public BackendImplementor create(String name, BackendBuildContext buildContext, ConfigurationPropertySource propertySource) { EventContext backendContext = EventContexts.fromBackendName( name ); @@ -128,15 +134,8 @@ private ElasticsearchAnalysisDefinitionRegistry getAnalysisDefinitionRegistry(Ev try { // Apply the user-provided analysis configurer if necessary final BeanProvider beanProvider = buildContext.getServiceManager().getBeanProvider(); - ConfigurationProperty> analysisConfigurerProperty = - ConfigurationProperty.forKey( SearchBackendElasticsearchSettings.ANALYSIS_CONFIGURER ) - .as( - ElasticsearchAnalysisConfigurer.class, - reference -> beanProvider - .getBean( ElasticsearchAnalysisConfigurer.class, reference ) - ) - .build(); - return analysisConfigurerProperty.get( propertySource ) + return ANALYSIS_CONFIGURER.get( propertySource ) + .map( beanReference -> beanReference.getBean( beanProvider, ElasticsearchAnalysisConfigurer.class ) ) .map( configurer -> { ElasticsearchAnalysisDefinitionContainerContextImpl collector = new ElasticsearchAnalysisDefinitionContainerContextImpl(); diff --git a/backend/lucene/src/main/java/org/hibernate/search/backend/lucene/impl/LuceneBackendFactory.java b/backend/lucene/src/main/java/org/hibernate/search/backend/lucene/impl/LuceneBackendFactory.java index 977a6c5979c..a765a1d11f8 100644 --- a/backend/lucene/src/main/java/org/hibernate/search/backend/lucene/impl/LuceneBackendFactory.java +++ b/backend/lucene/src/main/java/org/hibernate/search/backend/lucene/impl/LuceneBackendFactory.java @@ -32,6 +32,7 @@ import org.hibernate.search.engine.cfg.spi.ConfigurationProperty; import org.hibernate.search.engine.backend.spi.BackendBuildContext; import org.hibernate.search.engine.environment.bean.BeanProvider; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.util.EventContext; import org.hibernate.search.engine.logging.spi.EventContexts; import org.hibernate.search.util.AssertionFailure; @@ -69,6 +70,11 @@ public class LuceneBackendFactory implements BackendFactory { .withDefault( SearchBackendLuceneSettings.Defaults.MULTI_TENANCY_STRATEGY ) .build(); + private static final ConfigurationProperty> ANALYSIS_CONFIGURER = + ConfigurationProperty.forKey( SearchBackendLuceneSettings.ANALYSIS_CONFIGURER ) + .asBeanReference( LuceneAnalysisConfigurer.class ) + .build(); + @Override public BackendImplementor create(String name, BackendBuildContext buildContext, ConfigurationPropertySource propertySource) { @@ -158,15 +164,8 @@ private LuceneAnalysisDefinitionRegistry getAnalysisDefinitionRegistry(EventCont try { // Apply the user-provided analysis configurer if necessary final BeanProvider beanProvider = buildContext.getServiceManager().getBeanProvider(); - ConfigurationProperty> analysisConfigurerProperty = - ConfigurationProperty.forKey( SearchBackendLuceneSettings.ANALYSIS_CONFIGURER ) - .as( - LuceneAnalysisConfigurer.class, - reference -> beanProvider - .getBean( LuceneAnalysisConfigurer.class, reference ) - ) - .build(); - return analysisConfigurerProperty.get( propertySource ) + return ANALYSIS_CONFIGURER.get( propertySource ) + .map( beanReference -> beanReference.getBean( beanProvider, LuceneAnalysisConfigurer.class ) ) .map( configurer -> { LuceneAnalysisComponentFactory analysisComponentFactory = new LuceneAnalysisComponentFactory( luceneVersion, diff --git a/engine/src/main/java/org/hibernate/search/engine/cfg/impl/ConvertUtils.java b/engine/src/main/java/org/hibernate/search/engine/cfg/impl/ConvertUtils.java index 947eebc3e15..48f3ac1298d 100644 --- a/engine/src/main/java/org/hibernate/search/engine/cfg/impl/ConvertUtils.java +++ b/engine/src/main/java/org/hibernate/search/engine/cfg/impl/ConvertUtils.java @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.engine.logging.impl.Log; import org.hibernate.search.util.SearchException; import org.hibernate.search.util.impl.common.LoggerFactory; @@ -132,6 +133,29 @@ public static Optional convertLong(Object value) { throw log.invalidLongPropertyValue( "", null ); } + public static Optional convertBeanReference(Class expectedType, Object value) { + try { + if ( expectedType.isInstance( value ) ) { + return Optional.of( BeanReference.ofInstance( expectedType.cast( value ) ) ); + } + if ( value instanceof BeanReference ) { + return Optional.of( (BeanReference) value ); + } + if ( value instanceof Class ) { + return Optional.of( BeanReference.ofType( (Class) value ) ); + } + if ( value instanceof String ) { + return optionalTrimmedNonEmpty( (String) value ) + .map( BeanReference::ofName ); + } + } + catch (RuntimeException e) { + throw log.invalidBeanReferencePropertyValue( expectedType, e.getMessage(), e ); + } + + throw log.invalidBeanReferencePropertyValue( expectedType, "", null ); + } + public static Optional> convertMultiValue(Pattern separatorPattern, Function> elementConverter, Object value) { if ( value instanceof Collection ) { diff --git a/engine/src/main/java/org/hibernate/search/engine/cfg/impl/KeyContextImpl.java b/engine/src/main/java/org/hibernate/search/engine/cfg/impl/KeyContextImpl.java index 57c5b06854c..cd88e815115 100644 --- a/engine/src/main/java/org/hibernate/search/engine/cfg/impl/KeyContextImpl.java +++ b/engine/src/main/java/org/hibernate/search/engine/cfg/impl/KeyContextImpl.java @@ -10,6 +10,7 @@ import org.hibernate.search.engine.cfg.spi.KeyContext; import org.hibernate.search.engine.cfg.spi.OptionalPropertyContext; +import org.hibernate.search.engine.environment.bean.BeanReference; public class KeyContextImpl implements KeyContext { @@ -39,6 +40,11 @@ public OptionalPropertyContext asLong() { return new OptionalPropertyContextImpl<>( key, ConvertUtils::convertLong ); } + @Override + public OptionalPropertyContext asBeanReference(Class expectedType) { + return new OptionalPropertyContextImpl<>( key, v -> ConvertUtils.convertBeanReference( expectedType, v ) ); + } + @Override public OptionalPropertyContext as(Class expectedType, Function parser) { return new OptionalPropertyContextImpl<>( key, v -> ConvertUtils.convert( expectedType, parser, v ) ); diff --git a/engine/src/main/java/org/hibernate/search/engine/cfg/spi/KeyContext.java b/engine/src/main/java/org/hibernate/search/engine/cfg/spi/KeyContext.java index d9b50f92bcf..8839e598768 100644 --- a/engine/src/main/java/org/hibernate/search/engine/cfg/spi/KeyContext.java +++ b/engine/src/main/java/org/hibernate/search/engine/cfg/spi/KeyContext.java @@ -8,6 +8,8 @@ import java.util.function.Function; +import org.hibernate.search.engine.environment.bean.BeanReference; + public interface KeyContext { OptionalPropertyContext asString(); @@ -17,5 +19,7 @@ public interface KeyContext { OptionalPropertyContext asLong(); + OptionalPropertyContext asBeanReference(Class expectedType); + OptionalPropertyContext as(Class expectedType, Function parser); } diff --git a/engine/src/main/java/org/hibernate/search/engine/common/impl/IndexManagerBuildingStateHolder.java b/engine/src/main/java/org/hibernate/search/engine/common/impl/IndexManagerBuildingStateHolder.java index 4546ba61c9b..926e15a695e 100644 --- a/engine/src/main/java/org/hibernate/search/engine/common/impl/IndexManagerBuildingStateHolder.java +++ b/engine/src/main/java/org/hibernate/search/engine/common/impl/IndexManagerBuildingStateHolder.java @@ -12,6 +12,7 @@ import org.hibernate.search.engine.backend.document.DocumentElement; import org.hibernate.search.engine.backend.document.model.dsl.spi.IndexSchemaRootNodeBuilder; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.engine.mapper.mapping.spi.MappedIndexManager; import org.hibernate.search.engine.backend.index.spi.IndexManagerBuilder; import org.hibernate.search.engine.backend.index.spi.IndexManagerImplementor; @@ -36,8 +37,8 @@ class IndexManagerBuildingStateHolder { private static final ConfigurationProperty> INDEX_BACKEND_NAME = ConfigurationProperty.forKey( "backend" ).asString().build(); - private static final ConfigurationProperty> BACKEND_TYPE = - ConfigurationProperty.forKey( "type" ).asString().build(); + private static final ConfigurationProperty> BACKEND_TYPE = + ConfigurationProperty.forKey( "type" ).asBeanReference( BackendFactory.class ).build(); private final RootBuildContext rootBuildContext; private final ConfigurationPropertySource propertySource; @@ -94,11 +95,11 @@ void closeOnFailure(SuppressingCloser closer) { private BackendBuildingState createBackend(String backendName) { ConfigurationPropertySource backendPropertySource = propertySource.withMask( "backends." + backendName ); - // TODO more checks on the backend type (non-null, non-empty) - String backendType = BACKEND_TYPE.get( backendPropertySource ).get(); + // TODO properly check that there is a value before calling get() + BeanReference backendFactoryReference = BACKEND_TYPE.get( backendPropertySource ).get(); BeanProvider beanProvider = rootBuildContext.getServiceManager().getBeanProvider(); - BackendFactory backendFactory = beanProvider.getBean( BackendFactory.class, backendType ); + BackendFactory backendFactory = backendFactoryReference.getBean( beanProvider, BackendFactory.class ); BackendBuildContext backendBuildContext = new BackendBuildContextImpl( rootBuildContext ); BackendImplementor backend = backendFactory.create( backendName, backendBuildContext, backendPropertySource ); diff --git a/engine/src/main/java/org/hibernate/search/engine/environment/bean/BeanReference.java b/engine/src/main/java/org/hibernate/search/engine/environment/bean/BeanReference.java index 9a55a3650a4..9610bd308ce 100644 --- a/engine/src/main/java/org/hibernate/search/engine/environment/bean/BeanReference.java +++ b/engine/src/main/java/org/hibernate/search/engine/environment/bean/BeanReference.java @@ -55,4 +55,14 @@ static BeanReference of(Class type, String name) { return TypeAndNameBeanReference.createLenient( type, name ); } + /** + * Create a {@link BeanReference} referencing a bean instance directly. + * + * @param instance The bean instance. Must not be null. + * @return The corresponding {@link BeanReference}. + */ + static BeanReference ofInstance(Object instance) { + return new InstanceBeanReference( instance ); + } + } diff --git a/engine/src/main/java/org/hibernate/search/engine/environment/bean/InstanceBeanReference.java b/engine/src/main/java/org/hibernate/search/engine/environment/bean/InstanceBeanReference.java new file mode 100644 index 00000000000..f9c5fe22179 --- /dev/null +++ b/engine/src/main/java/org/hibernate/search/engine/environment/bean/InstanceBeanReference.java @@ -0,0 +1,30 @@ +/* + * 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.environment.bean; + +import org.hibernate.search.util.impl.common.Contracts; + +final class InstanceBeanReference implements BeanReference { + + private final Object instance; + + InstanceBeanReference(Object instance) { + Contracts.assertNotNull( instance, "instance" ); + this.instance = instance; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[instance=" + instance + "]"; + } + + @Override + public T getBean(BeanProvider beanProvider, Class expectedType) { + return expectedType.cast( instance ); + } + +} diff --git a/engine/src/main/java/org/hibernate/search/engine/logging/impl/Log.java b/engine/src/main/java/org/hibernate/search/engine/logging/impl/Log.java index 1eff696b678..0bf99d132fc 100644 --- a/engine/src/main/java/org/hibernate/search/engine/logging/impl/Log.java +++ b/engine/src/main/java/org/hibernate/search/engine/logging/impl/Log.java @@ -239,4 +239,9 @@ SearchException relativeFieldNameCannotContainDot(String relativeFieldName, @Message(id = ID_OFFSET_2 + 46, value = "Found an infinite IndexedEmbedded recursion involving path '%1$s' on type '%2$s'.") SearchException indexedEmbeddedCyclicRecursion(String cyclicRecursionPath, @FormatWith(MappableTypeModelFormatter.class) MappableTypeModel parentTypeModel); + + @Message(id = ID_OFFSET_2 + 47, + value = "Invalid BeanReference value: expected an instance of '%1$s', BeanReference, String or Class. %2$s") + SearchException invalidBeanReferencePropertyValue(@FormatWith(ClassFormatter.class) Class expectedType, + String nestedErrorMessage, @Cause Exception cause); } diff --git a/engine/src/test/java/org/hibernate/search/engine/cfg/spi/ConfigurationPropertyBeanReferenceTest.java b/engine/src/test/java/org/hibernate/search/engine/cfg/spi/ConfigurationPropertyBeanReferenceTest.java new file mode 100644 index 00000000000..c1ebbfb8652 --- /dev/null +++ b/engine/src/test/java/org/hibernate/search/engine/cfg/spi/ConfigurationPropertyBeanReferenceTest.java @@ -0,0 +1,312 @@ +/* + * 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.cfg.spi; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; + +import org.hibernate.search.engine.cfg.ConfigurationPropertySource; +import org.hibernate.search.engine.environment.bean.BeanProvider; +import org.hibernate.search.engine.environment.bean.BeanReference; +import org.hibernate.search.util.impl.test.SubTest; + +import org.junit.Test; + +import org.easymock.EasyMock; +import org.easymock.EasyMockSupport; + +public class ConfigurationPropertyBeanReferenceTest extends EasyMockSupport { + + private final ConfigurationPropertySource sourceMock = createMock( ConfigurationPropertySource.class ); + private final BeanProvider beanProviderMock = createMock( BeanProvider.class ); + + @Test + public void withDefault() { + String key = "withDefault"; + ConfigurationProperty property = + ConfigurationProperty.forKey( key ).asBeanReference( StubBean.class ) + .withDefault( BeanReference.ofName( "theDefault" ) ) + .build(); + + StubBean expected = new StubBeanImpl1(); + StubBean result; + + // No value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( Optional.empty() ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "theDefault" ) ) + .andReturn( expected ); + replayAll(); + result = property.get( sourceMock ).getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + + // String value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( "name" ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "name" ) ) + .andReturn( expected ); + replayAll(); + result = property.get( sourceMock ).getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + + // Class value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( BeanReference.ofType( StubBeanImpl1.class ) ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class ) ) + .andReturn( expected ); + replayAll(); + result = property.get( sourceMock ).getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + + // BeanReference value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( BeanReference.of( StubBeanImpl1.class, "name" ) ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class, "name" ) ) + .andReturn( expected ); + replayAll(); + result = property.get( sourceMock ).getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + } + + @Test + public void withoutDefault() { + String key = "withDefault"; + ConfigurationProperty> property = + ConfigurationProperty.forKey( key ).asBeanReference( StubBean.class ) + .build(); + + StubBean expected = new StubBeanImpl1(); + Optional reference; + StubBean result; + + // No value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( Optional.empty() ); + replayAll(); + reference = property.get( sourceMock ); + verifyAll(); + assertThat( reference ).isEmpty(); + + // String value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( "name" ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "name" ) ) + .andReturn( expected ); + replayAll(); + reference = property.get( sourceMock ); + assertThat( reference ).isNotEmpty(); + result = reference.get().getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + + // Class value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( BeanReference.ofType( StubBeanImpl1.class ) ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class ) ) + .andReturn( expected ); + replayAll(); + reference = property.get( sourceMock ); + assertThat( reference ).isNotEmpty(); + result = reference.get().getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + + // BeanReference value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( BeanReference.of( StubBeanImpl1.class, "name" ) ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class, "name" ) ) + .andReturn( expected ); + replayAll(); + reference = property.get( sourceMock ); + assertThat( reference ).isNotEmpty(); + result = reference.get().getBean( beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).isEqualTo( expected ); + } + + @Test + public void multiValued() { + String key = "multiValued"; + ConfigurationProperty>> property = + ConfigurationProperty.forKey( key ).asBeanReference( StubBean.class ) + .multivalued( Pattern.compile( " " ) ) + .build(); + + StubBean expected1 = new StubBeanImpl1(); + StubBean expected2 = new StubBeanImpl2(); + Optional> references; + List result; + + // No value + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( Optional.empty() ); + replayAll(); + references = property.get( sourceMock ); + verifyAll(); + assertThat( references ).isEmpty(); + + // String value - one + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( "name" ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "name" ) ) + .andReturn( expected1 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1 ); + + // String value - multiple + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( "name1 name2" ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "name1" ) ) + .andReturn( expected1 ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, "name2" ) ) + .andReturn( expected2 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1, expected2 ); + + // Class value - one + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( + createCollection( StubBeanImpl1.class ) + ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class ) ) + .andReturn( expected1 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1 ); + + // Class value - multiple + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( + createCollection( StubBeanImpl1.class, StubBeanImpl2.class ) + ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class ) ) + .andReturn( expected1 ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl2.class ) ) + .andReturn( expected2 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1, expected2 ); + + // BeanReference value - one + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( + createCollection( BeanReference.of( StubBeanImpl1.class, "name" ) ) + ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class, "name" ) ) + .andReturn( expected1 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1 ); + + // BeanReference value - multiple + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( + createCollection( + BeanReference.of( StubBeanImpl1.class, "name1" ), + BeanReference.of( StubBeanImpl2.class, "name2" ) + ) + ) ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl1.class, "name1" ) ) + .andReturn( expected1 ); + EasyMock.expect( beanProviderMock.getBean( StubBean.class, StubBeanImpl2.class, "name2" ) ) + .andReturn( expected2 ); + replayAll(); + references = property.get( sourceMock ); + assertThat( references ).isNotEmpty(); + result = getAllBeans( references.get(), beanProviderMock, StubBean.class ); + verifyAll(); + assertThat( result ).containsExactly( expected1, expected2 ); + } + + @Test + public void invalidType() { + String key = "invalidType"; + String resolvedKey = "some.prefix." + key; + ConfigurationProperty> property = + ConfigurationProperty.forKey( key ).asBeanReference( StubBean.class ) + .build(); + + InvalidType invalidTypeValue = new InvalidType(); + resetAll(); + EasyMock.expect( sourceMock.get( key ) ).andReturn( (Optional) Optional.of( invalidTypeValue ) ); + EasyMock.expect( sourceMock.resolve( key ) ).andReturn( Optional.of( resolvedKey ) ); + replayAll(); + SubTest.expectException( () -> property.get( sourceMock ) ) + .assertThrown() + .hasMessageContaining( + "Unable to convert configuration property '" + resolvedKey + + "' with value '" + invalidTypeValue + "':" + ) + .hasMessageContaining( + "Invalid BeanReference value: expected an instance of '" + StubBean.class.getName() + + "', BeanReference, String or Class" + ); + verifyAll(); + } + + @SafeVarargs + private static Collection createCollection(T... values) { + // Don't create a List, that would be too easy. + Collection collection = new LinkedHashSet<>(); + Collections.addAll( collection, values ); + return collection; + } + + private static List getAllBeans(List beanReferences, BeanProvider beanProviderMock, + Class expectedType) { + List beans = new ArrayList<>(); + for ( BeanReference beanReference : beanReferences ) { + beans.add( beanReference.getBean( beanProviderMock, expectedType ) ); + } + return beans; + } + + private interface StubBean { + } + + private class StubBeanImpl1 implements StubBean { + } + + private class StubBeanImpl2 implements StubBean { + } + + private static class InvalidType { + @Override + public String toString() { + return getClass().getSimpleName(); + } + } + +} diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMappingInitiator.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMappingInitiator.java index e4e172ebb41..ea02b9412e2 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMappingInitiator.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/impl/HibernateOrmMappingInitiator.java @@ -18,6 +18,7 @@ import org.hibernate.search.engine.cfg.ConfigurationPropertySource; import org.hibernate.search.engine.cfg.spi.ConfigurationProperty; import org.hibernate.search.engine.environment.bean.BeanProvider; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.engine.mapper.mapping.spi.MappingBuildContext; import org.hibernate.search.engine.mapper.mapping.building.spi.MappingConfigurationCollector; import org.hibernate.search.mapper.orm.cfg.SearchOrmSettings; @@ -45,6 +46,11 @@ public class HibernateOrmMappingInitiator extends AbstractPojoMappingInitiator> MAPPING_CONFIGURER = + ConfigurationProperty.forKey( SearchOrmSettings.Radicals.MAPPING_CONFIGURER ) + .asBeanReference( HibernateOrmSearchMappingConfigurer.class ) + .build(); + public static HibernateOrmMappingInitiator create(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor) { HibernateOrmBootstrapIntrospector introspector = @@ -108,15 +114,8 @@ public void configure(MappingBuildContext buildContext, ConfigurationPropertySou // Apply the user-provided mapping configurer if necessary final BeanProvider beanProvider = buildContext.getServiceManager().getBeanProvider(); - ConfigurationProperty> mappingConfigurerProperty = - ConfigurationProperty.forKey( SearchOrmSettings.Radicals.MAPPING_CONFIGURER ) - .as( - HibernateOrmSearchMappingConfigurer.class, - reference -> beanProvider.getBean( - HibernateOrmSearchMappingConfigurer.class, reference ) - ) - .build(); - mappingConfigurerProperty.get( propertySource ) + MAPPING_CONFIGURER.get( propertySource ) + .map( beanReference -> beanReference.getBean( beanProvider, HibernateOrmSearchMappingConfigurer.class ) ) .ifPresent( configurer -> configurer.configure( this ) ); super.configure( buildContext, propertySource, configurationCollector );