Skip to content

Commit

Permalink
Introduce @SearchExtension to configure Hibernate Search through anno…
Browse files Browse the repository at this point in the history
…tated beans
  • Loading branch information
yrodiere committed Jun 23, 2022
1 parent ac262f3 commit ea0b1fd
Show file tree
Hide file tree
Showing 15 changed files with 524 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.hibernate.search.orm.coordination.outboxpolling.deployment;

import java.util.List;
import java.util.Optional;

import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings;
import org.hibernate.search.mapper.orm.coordination.outboxpolling.mapping.spi.HibernateOrmMapperOutboxPollingClasses;
Expand Down Expand Up @@ -45,8 +46,7 @@ void setStaticConfig(HibernateSearchOutboxPollingRecorder recorder,
List<HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem> configuredPersistenceUnits,
BuildProducer<HibernateSearchIntegrationStaticConfiguredBuildItem> staticConfigured) {
for (HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem configuredPersistenceUnit : configuredPersistenceUnits) {
if (!configuredPersistenceUnit.getBuildTimeConfig().coordination.strategy
.equals(HibernateOrmMapperOutboxPollingSettings.COORDINATION_STRATEGY_NAME)) {
if (!isUsingOutboxPolling(configuredPersistenceUnit)) {
continue;
}
String puName = configuredPersistenceUnit.getPersistenceUnitName();
Expand All @@ -65,8 +65,7 @@ void setRuntimeConfig(HibernateSearchOutboxPollingRecorder recorder,
List<HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem> configuredPersistenceUnits,
BuildProducer<HibernateSearchIntegrationRuntimeConfiguredBuildItem> runtimeConfigured) {
for (HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem configuredPersistenceUnit : configuredPersistenceUnits) {
if (!configuredPersistenceUnit.getBuildTimeConfig().coordination.strategy
.equals(HibernateOrmMapperOutboxPollingSettings.COORDINATION_STRATEGY_NAME)) {
if (!isUsingOutboxPolling(configuredPersistenceUnit)) {
continue;
}
String puName = configuredPersistenceUnit.getPersistenceUnitName();
Expand All @@ -76,4 +75,10 @@ void setRuntimeConfig(HibernateSearchOutboxPollingRecorder recorder,
}
}

private boolean isUsingOutboxPolling(HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem persistenceUnit) {
Optional<String> configuredStrategy = persistenceUnit.getBuildTimeConfig().coordination.strategy;
return configuredStrategy.isPresent()
&& configuredStrategy.get().equals(HibernateOrmMapperOutboxPollingSettings.COORDINATION_STRATEGY_NAME);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.hibernate.search.orm.elasticsearch.deployment;

import org.jboss.jandex.DotName;

class ClassNames {

static final DotName SEARCH_EXTENSION = DotName
.createSimple("io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension");
static final DotName INDEXED = DotName
.createSimple("org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed");

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.hibernate.search.mapper.orm.mapping.SearchMapping;
import org.hibernate.search.mapper.orm.session.SearchSession;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.deployment.annotations.BuildProducer;
Expand Down Expand Up @@ -64,4 +66,21 @@ private static <T> SyntheticBeanBuildItem createSyntheticBean(String persistence

return configurator.done();
}

@BuildStep
void registerAnnotations(BuildProducer<AdditionalBeanBuildItem> additionalBeans,
BuildProducer<BeanDefiningAnnotationBuildItem> beanDefiningAnnotations) {
// add the @SearchExtension class
// otherwise it won't be registered as qualifier
additionalBeans.produce(AdditionalBeanBuildItem.builder()
.addBeanClasses(ClassNames.SEARCH_EXTENSION.toString())
.build());

// Register the default scope for @SearchExtension and make such beans unremovable by default
// TODO make @SearchExtension beans unremovable only if the corresponding PU actually exists and is enabled
// (I think there's a feature request for a configuration property to disable a PU at runtime?)
beanDefiningAnnotations
.produce(new BeanDefiningAnnotationBuildItem(ClassNames.SEARCH_EXTENSION, DotNames.APPLICATION_SCOPED,
false));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.hibernate.search.orm.elasticsearch.deployment;

import java.util.Map;
import java.util.Set;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit;

Expand All @@ -8,15 +11,18 @@ public final class HibernateSearchElasticsearchPersistenceUnitConfiguredBuildIte
private final String persistenceUnitName;
private final HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig;
private final boolean defaultBackendIsUsed;
private Map<String, Set<String>> backendAndIndexNamesForSearchExtensions;

public HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem(String persistenceUnitName,
HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig, boolean defaultBackendIsUsed) {
HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit buildTimeConfig, boolean defaultBackendIsUsed,
Map<String, Set<String>> backendAndIndexNamesForSearchExtensions) {
if (persistenceUnitName == null) {
throw new IllegalArgumentException("persistenceUnitName cannot be null");
}
this.persistenceUnitName = persistenceUnitName;
this.buildTimeConfig = buildTimeConfig;
this.defaultBackendIsUsed = defaultBackendIsUsed;
this.backendAndIndexNamesForSearchExtensions = backendAndIndexNamesForSearchExtensions;
}

public String getPersistenceUnitName() {
Expand All @@ -31,6 +37,10 @@ public boolean isDefaultBackendUsed() {
return defaultBackendIsUsed;
}

public Map<String, Set<String>> getBackendAndIndexNamesForSearchExtensions() {
return backendAndIndexNamesForSearchExtensions;
}

@Override
public String toString() {
return getClass().getSimpleName() + " [" + persistenceUnitName + "]";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.quarkus.hibernate.search.orm.elasticsearch.deployment;

import static io.quarkus.hibernate.search.orm.elasticsearch.deployment.HibernateSearchClasses.INDEXED;
import static io.quarkus.hibernate.search.orm.elasticsearch.deployment.ClassNames.INDEXED;
import static io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.backendPropertyKey;
import static io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.elasticsearchVersionPropertyKey;
import static io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchElasticsearchRuntimeConfig.mapperPropertyKey;
Expand All @@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -100,6 +101,9 @@ public void build(HibernateSearchElasticsearchRecorder recorder,
IndexView index = combinedIndexBuildItem.getIndex();
Collection<AnnotationInstance> indexedAnnotations = index.getAnnotations(INDEXED);

Map<String, Map<String, Set<String>>> persistenceUnitAndBackendAndIndexNamesForSearchExtensions = collectPersistenceUnitAndBackendAndIndexNamesForSearchExtensions(
index);

for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) {
Collection<AnnotationInstance> indexedAnnotationsForPU = new ArrayList<>();
for (AnnotationInstance indexedAnnotation : indexedAnnotations) {
Expand All @@ -108,15 +112,37 @@ public void build(HibernateSearchElasticsearchRecorder recorder,
indexedAnnotationsForPU.add(indexedAnnotation);
}
}
Map<String, Set<String>> backendAndIndexNamesForSearchExtensions = persistenceUnitAndBackendAndIndexNamesForSearchExtensions
.getOrDefault(puDescriptor.getPersistenceUnitName(), Collections.emptyMap());
buildForPersistenceUnit(recorder, indexedAnnotationsForPU, puDescriptor.getPersistenceUnitName(),
backendAndIndexNamesForSearchExtensions,
configuredPersistenceUnits, staticIntegrations, runtimeIntegrations);
}

registerReflectionForGson(reflectiveClass);
}

public static Map<String, Map<String, Set<String>>> collectPersistenceUnitAndBackendAndIndexNamesForSearchExtensions(
IndexView index) {
Map<String, Map<String, Set<String>>> result = new LinkedHashMap<>();
for (AnnotationInstance annotation : index.getAnnotations(ClassNames.SEARCH_EXTENSION)) {
var puName = annotation.value("persistenceUnit");
var backendName = annotation.value("backend");
var indexName = annotation.value("index");
Set<String> indexNames = result
.computeIfAbsent(puName == null ? PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME : puName.asString(),
ignored -> new LinkedHashMap<>())
.computeIfAbsent(backendName == null ? null : backendName.asString(), ignored -> new LinkedHashSet<>());
if (indexName != null) {
indexNames.add(indexName.asString());
}
}
return result;
}

private void buildForPersistenceUnit(HibernateSearchElasticsearchRecorder recorder,
Collection<AnnotationInstance> indexedAnnotationsForPU, String persistenceUnitName,
Map<String, Set<String>> backendAndIndexNamesForSearchExtensions,
BuildProducer<HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem> configuredPersistenceUnits,
BuildProducer<HibernateOrmIntegrationStaticConfiguredBuildItem> staticIntegrations,
BuildProducer<HibernateOrmIntegrationRuntimeConfiguredBuildItem> runtimeIntegrations) {
Expand All @@ -127,7 +153,7 @@ private void buildForPersistenceUnit(HibernateSearchElasticsearchRecorder record
// we need a runtime listener even when Hibernate Search is disabled,
// just to let Hibernate Search boot up until the point where it checks whether it's enabled or not
runtimeIntegrations.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH,
persistenceUnitName).setInitListener(recorder.createDisabledRuntimeInitListener()));
persistenceUnitName).setInitListener(recorder.createDisabledRuntimeInitListener(persistenceUnitName)));
return;
}

Expand All @@ -146,7 +172,7 @@ private void buildForPersistenceUnit(HibernateSearchElasticsearchRecorder record

configuredPersistenceUnits
.produce(new HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem(persistenceUnitName, puConfig,
defaultBackendIsUsed));
defaultBackendIsUsed, backendAndIndexNamesForSearchExtensions));
}

@BuildStep
Expand All @@ -168,7 +194,7 @@ void setStaticConfig(RecorderContext recorderContext, HibernateSearchElasticsear
List<HibernateSearchIntegrationStaticConfiguredBuildItem> integrationStaticConfigBuildItems,
List<HibernateSearchElasticsearchPersistenceUnitConfiguredBuildItem> configuredPersistenceUnits,
BuildProducer<HibernateOrmIntegrationStaticConfiguredBuildItem> staticConfigured) {
// Make it possible to record the ElasticsearchVersion as bytecode:
// Make it possible to record the settings as bytecode:
recorderContext.registerSubstitution(ElasticsearchVersion.class,
String.class, ElasticsearchVersionSubstitution.class);

Expand All @@ -189,8 +215,10 @@ void setStaticConfig(RecorderContext recorderContext, HibernateSearchElasticsear
}
staticConfigured.produce(
new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, puName)
.setInitListener(recorder.createStaticInitListener(configuredPersistenceUnit.getBuildTimeConfig(),
integrationStaticInitListeners))
.setInitListener(
recorder.createStaticInitListener(puName, configuredPersistenceUnit.getBuildTimeConfig(),
configuredPersistenceUnit.getBackendAndIndexNamesForSearchExtensions(),
integrationStaticInitListeners))
.setXmlMappingRequired(xmlMappingRequired));
}
}
Expand All @@ -217,6 +245,7 @@ void setRuntimeConfig(HibernateSearchElasticsearchRecorder recorder,
new HibernateOrmIntegrationRuntimeConfiguredBuildItem(HIBERNATE_SEARCH_ELASTICSEARCH, puName)
.setInitListener(
recorder.createRuntimeInitListener(runtimeConfig, puName,
configuredPersistenceUnit.getBackendAndIndexNamesForSearchExtensions(),
integrationRuntimeInitListeners)));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.quarkus.hibernate.search.orm.elasticsearch;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Qualifier;

import io.quarkus.hibernate.orm.PersistenceUnit;

/**
* CDI qualifier for beans representing an "extension" of Hibernate Search in a given persistence unit,
* i.e. beans injected into Hibernate Search as part of its configuration.
* <p>
* See the reference documentation for information about extensions that supports this annotation.
*/
@Target({ TYPE, FIELD, METHOD, PARAMETER })
@Retention(RUNTIME)
@Documented
@Qualifier
@Repeatable(SearchExtension.List.class)
public @interface SearchExtension {

/**
* @return The name of the persistence unit that the qualified bean should be assigned to.
*/
String persistenceUnit() default PersistenceUnit.DEFAULT;

/**
* @return The name of the Hibernate Search backend that the qualified bean should be assigned to.
*/
String backend() default "";

/**
* @return The name of the Hibernate Search index that the qualified bean should be assigned to.
*/
String index() default "";

class Literal
extends AnnotationLiteral<SearchExtension>
implements SearchExtension {

private final String persistenceUnit;
private final String backend;
private final String index;

public Literal(String persistenceUnit, String backend, String index) {
this.persistenceUnit = persistenceUnit;
this.backend = backend;
this.index = index;
}

@Override
public String persistenceUnit() {
return persistenceUnit;
}

@Override
public String backend() {
return backend;
}

@Override
public String index() {
return index;
}
}

@Target({ TYPE, FIELD, METHOD, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
SearchExtension[] value();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public class HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit {
*
* The referenced bean must implement `FailureHandler`.
*
* [NOTE]
* ====
* Instead of setting this configuration property,
* you can simply annotate your custom `FailureHandler` implementation with `@SearchExtension`
* and leave the configuration property unset: Hibernate Search will use the annotated implementation automatically.
* If this configuration property is set, it takes precedence over any `@SearchExtension` annotation.
* ====
*
* @asciidoclet
*/
@ConfigItem
Expand Down Expand Up @@ -171,6 +179,14 @@ public static class AnalysisConfig {
*
* See <<analysis-configurer>> for more information.
*
* [NOTE]
* ====
* Instead of setting this configuration property,
* you can simply annotate your custom `ElasticsearchAnalysisConfigurer` implementation with `@SearchExtension`
* and leave the configuration property unset: Hibernate Search will use the annotated implementation automatically.
* If this configuration property is set, it takes precedence over any `@SearchExtension` annotation.
* ====
*
* @asciidoclet
*/
@ConfigItem
Expand Down Expand Up @@ -200,6 +216,14 @@ public static class LayoutConfig {
* link:{hibernate-search-doc-prefix}#backend-elasticsearch-indexlayout[this section of the reference documentation]
* for more information.
*
* [NOTE]
* ====
* Instead of setting this configuration property,
* you can simply annotate your custom `IndexLayoutStrategy` implementation with `@SearchExtension`
* and leave the configuration property unset: Hibernate Search will use the annotated implementation automatically.
* If this configuration property is set, it takes precedence over any `@SearchExtension` annotation.
* ====
*
* @asciidoclet
*/
@ConfigItem
Expand All @@ -218,7 +242,7 @@ public static class CoordinationConfig {
* @asciidoclet
*/
@ConfigItem(defaultValue = "none")
public String strategy;
public Optional<String> strategy;
}

}

0 comments on commit ea0b1fd

Please sign in to comment.