Skip to content

Commit

Permalink
HSEARCH-4615 Create configuration context and configurer for Standalo…
Browse files Browse the repository at this point in the history
…ne POJO mapper
  • Loading branch information
marko-bekhta authored and yrodiere committed Dec 22, 2022
1 parent 633da08 commit d4b2405
Show file tree
Hide file tree
Showing 8 changed files with 494 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ include::{sourcedir}/org/hibernate/search/documentation/mapper/pojo/standalone/e
<4> Build the `SearchMapping`.
====

Alternatively a configurer class implementing `StandalonePojoMappingConfigurer` can be provided.

.Starting up Hibernate Search with the Standalone POJO Mapper and Configurer
====
[source, JAVA, indent=0]
----
include::{sourcedir}/org/hibernate/search/documentation/mapper/pojo/standalone/entrypoints/StandalonePojoConfigurer.java[tags=configurer]
----
<1> Declare *all* entity types that Hibernate Search may need to work with.
<2> Set additional configuration exposed through the context.
----
include::{sourcedir}/org/hibernate/search/documentation/mapper/pojo/standalone/entrypoints/StandalonePojoConfigurerIT.java[tags=setup]
----
<1> Create a builder.
<2> Set configurer.
<3> Set additional configuration properties (see also <<configuration>>).
<4> Build the `SearchMapping`.
====

[[mapper-pojo-standalone-shutdown]]
== Shutdown

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.documentation.mapper.pojo.standalone.entrypoints;

import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurationContext;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer;

// tag::configurer[]

public class StandalonePojoConfigurer implements StandalonePojoMappingConfigurer {
@Override
public void configure(StandalonePojoMappingConfigurationContext context) {
context.addEntityTypes( Book.class, Associate.class, Manager.class );// <1>

context.defaultReindexOnUpdate( ReindexOnUpdate.SHALLOW ); // <2>
context.multiTenancyEnabled( true );
}
}
// end::configurer[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.documentation.mapper.pojo.standalone.entrypoints;

import static org.assertj.core.api.Assertions.assertThat;

import org.hibernate.search.documentation.testsupport.BackendConfigurations;
import org.hibernate.search.documentation.testsupport.TestConfiguration;
import org.hibernate.search.mapper.pojo.standalone.entity.SearchIndexedEntity;
import org.hibernate.search.mapper.pojo.standalone.mapping.CloseableSearchMapping;
import org.hibernate.search.mapper.pojo.standalone.mapping.SearchMapping;
import org.hibernate.search.util.impl.integrationtest.common.TestConfigurationProvider;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

public class StandalonePojoConfigurerIT {

private CloseableSearchMapping theSearchMapping;

@Rule
public TestConfigurationProvider configurationProvider = new TestConfigurationProvider();

@Before
public void setup() {
// tag::setup[]
CloseableSearchMapping searchMapping = SearchMapping.builder() // <1>
.property(
"hibernate.search.mapping.configurer", // <2>
"class:org.hibernate.search.documentation.mapper.pojo.standalone.entrypoints.StandalonePojoConfigurer"
)
.property(
"hibernate.search.backend.hosts", // <3>
"elasticsearch.mycompany.com"
)
// end::setup[]
.properties( TestConfiguration.standalonePojoMapperProperties(
configurationProvider,
BackendConfigurations.simple()
) )
// tag::setup[]
.build(); // <4>
// end::setup[]
this.theSearchMapping = searchMapping;
}

@After
public void cleanup() {
if ( theSearchMapping != null ) {
theSearchMapping.close();
}
}

@Test
public void mappingContainsExpectedEntities() {
assertThat( theSearchMapping.allIndexedEntities() )
.extracting( SearchIndexedEntity::name )
.contains( "Book", "Associate", "Manager" )
;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* 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.integrationtest.mapper.pojo.mapping.definition;

import java.lang.invoke.MethodHandles;

import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;
import org.hibernate.search.mapper.pojo.standalone.cfg.StandalonePojoMapperSettings;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurationContext;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer;
import org.hibernate.search.util.impl.integrationtest.common.rule.BackendMock;
import org.hibernate.search.util.impl.integrationtest.mapper.pojo.standalone.StandalonePojoMappingSetupHelper;
import org.hibernate.search.util.impl.test.annotation.TestForIssue;

import org.junit.Rule;
import org.junit.Test;

public class PojoStandaloneSearchMappingConfigurerIT {

@Rule
public BackendMock backendMock = new BackendMock();

@Rule
public StandalonePojoMappingSetupHelper setupHelper = StandalonePojoMappingSetupHelper.withBackendMock( MethodHandles.lookup(), backendMock );

@Test
public void none() {
backendMock.expectSchema( IndexedEntity.INDEX, b -> b
.field( "annotationMapped", String.class )
);

setupHelper.start()
.setup( IndexedEntity.class );
backendMock.verifyExpectationsMet();
}

@Test
public void single() {
backendMock.expectSchema( IndexedEntity.INDEX, b -> b
.field( "annotationMapped", String.class )
.field( "nonAnnotationMapped1", String.class )
);

setupHelper.start()
.expectCustomBeans()
.withProperty( StandalonePojoMapperSettings.MAPPING_CONFIGURER,
MappingConfigurer1.class.getName() )
.setup( IndexedEntity.class );
backendMock.verifyExpectationsMet();
}

public static class MappingConfigurer1 implements StandalonePojoMappingConfigurer {
@Override
public void configure(StandalonePojoMappingConfigurationContext context) {
context.programmaticMapping().type( IndexedEntity.class )
.property( "nonAnnotationMapped1" )
.keywordField();
}
}

@Test
@TestForIssue(jiraKey = "HSEARCH-4594")
public void multiple() {
backendMock.expectSchema( IndexedEntity.INDEX, b -> b
.field( "annotationMapped", String.class )
.field( "nonAnnotationMapped1", String.class )
.field( "nonAnnotationMapped2", String.class )
);

setupHelper.start()
.expectCustomBeans()
.withProperty( StandalonePojoMapperSettings.MAPPING_CONFIGURER,
MappingConfigurer1.class.getName() + "," + MappingConfigurer2.class.getName() )
.setup( IndexedEntity.class );
backendMock.verifyExpectationsMet();
}

public static class MappingConfigurer2 implements StandalonePojoMappingConfigurer {
@Override
public void configure(StandalonePojoMappingConfigurationContext context) {
context.programmaticMapping().type( IndexedEntity.class )
.property( "nonAnnotationMapped2" )
.keywordField();
}
}

@Indexed(index = IndexedEntity.INDEX)
public static final class IndexedEntity {
public static final String INDEX = "IndexedEntity";

@DocumentId
private Integer id;

@KeywordField
private String annotationMapped;

private String nonAnnotationMapped1;

private String nonAnnotationMapped2;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getAnnotationMapped() {
return annotationMapped;
}

public void setAnnotationMapped(String annotationMapped) {
this.annotationMapped = annotationMapped;
}

public String getNonAnnotationMapped1() {
return nonAnnotationMapped1;
}

public void setNonAnnotationMapped1(String nonAnnotationMapped1) {
this.nonAnnotationMapped1 = nonAnnotationMapped1;
}

public String getNonAnnotationMapped2() {
return nonAnnotationMapped2;
}

public void setNonAnnotationMapped2(String nonAnnotationMapped2) {
this.nonAnnotationMapped2 = nonAnnotationMapped2;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.mapper.pojo.standalone.cfg;

import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer;
import org.hibernate.search.mapper.pojo.standalone.schema.management.SchemaManagementStrategyName;
import org.hibernate.search.util.common.annotation.Incubating;

Expand All @@ -29,12 +30,26 @@ private StandalonePojoMapperSettings() {
*/
public static final String SCHEMA_MANAGEMENT_STRATEGY = PREFIX + Radicals.SCHEMA_MANAGEMENT_STRATEGY;

/**
* The mapping configurer to use.
* <p>
* Expects a single-valued or multi-valued reference to beans of type {@link StandalonePojoMappingConfigurer}.
* <p>
* Defaults to no value.
*
* @see org.hibernate.search.engine.cfg The core documentation of configuration properties,
* which includes a description of the "multi-valued bean reference" properties and accepted values.
*/
public static final String MAPPING_CONFIGURER = PREFIX + Radicals.MAPPING_CONFIGURER;


public static class Radicals {

private Radicals() {
}

public static final String SCHEMA_MANAGEMENT_STRATEGY = "schema_management.strategy";
public static final String MAPPING_CONFIGURER = "mapping.configurer";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
*/
package org.hibernate.search.mapper.pojo.standalone.impl;

import java.util.List;

import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.engine.environment.bean.BeanHolder;
import org.hibernate.search.engine.environment.bean.BeanReference;
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.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.pojo.standalone.cfg.StandalonePojoMapperSettings;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurationContext;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer;
import org.hibernate.search.mapper.pojo.standalone.mapping.impl.StandalonePojoMapperDelegate;
import org.hibernate.search.mapper.pojo.standalone.mapping.impl.StandalonePojoMappingPartialBuildState;
import org.hibernate.search.mapper.pojo.standalone.mapping.metadata.EntityConfigurer;
Expand All @@ -21,12 +29,19 @@
import org.hibernate.search.mapper.pojo.mapping.building.spi.PojoTypeMetadataContributor;
import org.hibernate.search.mapper.pojo.mapping.spi.AbstractPojoMappingInitiator;

public class StandalonePojoMappingInitiator extends AbstractPojoMappingInitiator<StandalonePojoMappingPartialBuildState> {
public class StandalonePojoMappingInitiator extends AbstractPojoMappingInitiator<StandalonePojoMappingPartialBuildState>
implements StandalonePojoMappingConfigurationContext {

private static final ConfigurationProperty<SchemaManagementStrategyName> SCHEMA_MANAGEMENT_STRATEGY
= ConfigurationProperty.forKey( StandalonePojoMapperSettings.Radicals.SCHEMA_MANAGEMENT_STRATEGY )
.as( SchemaManagementStrategyName.class, SchemaManagementStrategyName::of )
.withDefault( StandalonePojoMapperSettings.Defaults.SCHEMA_MANAGEMENT_STRATEGY )
.as( SchemaManagementStrategyName.class, SchemaManagementStrategyName::of )
.withDefault( StandalonePojoMapperSettings.Defaults.SCHEMA_MANAGEMENT_STRATEGY )
.build();

private static final OptionalConfigurationProperty<List<BeanReference<? extends StandalonePojoMappingConfigurer>>> MAPPING_CONFIGURER =
ConfigurationProperty.forKey( StandalonePojoMapperSettings.Radicals.MAPPING_CONFIGURER )
.asBeanReference( StandalonePojoMappingConfigurer.class )
.multivalued()
.build();

private final StandalonePojoEntityTypeMetadataProvider.Builder entityTypeMetadataProviderBuilder;
Expand All @@ -39,25 +54,44 @@ public StandalonePojoMappingInitiator(StandalonePojoBootstrapIntrospector intros
entityTypeMetadataProviderBuilder = new StandalonePojoEntityTypeMetadataProvider.Builder( introspector );
}

public <E> void addEntityType(Class<E> clazz, String entityName, EntityConfigurer<E> configurerOrNull) {
public <E> StandalonePojoMappingInitiator addEntityType(Class<E> clazz, String entityName, EntityConfigurer<E> configurerOrNull) {
entityTypeMetadataProviderBuilder.addEntityType( clazz, entityName, configurerOrNull );

return this;
}

@Override
public void configure(MappingBuildContext buildContext,
MappingConfigurationCollector<PojoTypeMetadataContributor> configurationCollector) {
// Apply the user-provided mapping configurer if necessary.
// Has to happen before building entityTypeMetadataProvider as configurers can add more entities.
MAPPING_CONFIGURER.getAndMap( buildContext.configurationPropertySource(), buildContext.beanResolver()::resolve )
.ifPresent( holder -> {
try ( BeanHolder<List<StandalonePojoMappingConfigurer>> configurerHolder = holder ) {
for ( StandalonePojoMappingConfigurer configurer : configurerHolder.get() ) {
configurer.configure( this );
}
}
} );

entityTypeMetadataProvider = entityTypeMetadataProviderBuilder.build();

SchemaManagementStrategyName schemaManagementStrategyName = SCHEMA_MANAGEMENT_STRATEGY.get(
buildContext.configurationPropertySource() );
schemaManagementListener = new SchemaManagementListener( schemaManagementStrategyName );

addConfigurationContributor( new StandalonePojoTypeConfigurationContributor( entityTypeMetadataProvider ) );

super.configure( buildContext, configurationCollector );
}

@Override
protected PojoMapperDelegate<StandalonePojoMappingPartialBuildState> createMapperDelegate() {
return new StandalonePojoMapperDelegate( entityTypeMetadataProvider, schemaManagementListener );
}

@Override
public void multiTenancyEnabled(boolean multiTenancyEnabled) {
this.tenancyMode( multiTenancyEnabled ? TenancyMode.MULTI_TENANCY : TenancyMode.SINGLE_TENANCY );
}
}

0 comments on commit d4b2405

Please sign in to comment.