Skip to content

Commit

Permalink
HSEARCH-3386 Make sure that errors occurring when querying properties…
Browse files Browse the repository at this point in the history
… include some context

In particular:

* if an error occurs while retrieving a bean, we should display the
property key and the bean reference;
* if an error occurs because a mandatory property was missing, we should
display the property key.
  • Loading branch information
yrodiere committed Dec 4, 2018
1 parent d4254f7 commit 13eac3a
Show file tree
Hide file tree
Showing 17 changed files with 266 additions and 58 deletions.
Expand Up @@ -26,6 +26,7 @@
import org.hibernate.search.backend.elasticsearch.gson.impl.GsonProvider;
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.util.impl.common.SearchThreadFactory;

/**
Expand All @@ -40,12 +41,12 @@ public class ElasticsearchClientFactoryImpl implements ElasticsearchClientFactor
.withDefault( SearchBackendElasticsearchSettings.Defaults.HOST )
.build();

private static final ConfigurationProperty<Optional<String>> USERNAME =
private static final OptionalConfigurationProperty<String> USERNAME =
ConfigurationProperty.forKey( SearchBackendElasticsearchSettings.USERNAME )
.asString()
.build();

private static final ConfigurationProperty<Optional<String>> PASSWORD =
private static final OptionalConfigurationProperty<String> PASSWORD =
ConfigurationProperty.forKey( SearchBackendElasticsearchSettings.PASSWORD )
.asString()
.build();
Expand Down
Expand Up @@ -8,7 +8,6 @@

import java.lang.invoke.MethodHandles;
import java.util.Locale;
import java.util.Optional;

import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer;
import org.hibernate.search.backend.elasticsearch.analysis.model.dsl.impl.ElasticsearchAnalysisDefinitionContainerContextImpl;
Expand Down Expand Up @@ -37,6 +36,7 @@
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.backend.spi.BackendBuildContext;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.engine.environment.bean.BeanProvider;
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.logging.spi.EventContexts;
Expand Down Expand Up @@ -67,7 +67,7 @@ public class ElasticsearchBackendFactory implements BackendFactory {
.withDefault( SearchBackendElasticsearchSettings.Defaults.LOG_JSON_PRETTY_PRINTING )
.build();

private static final ConfigurationProperty<Optional<BeanReference<? extends ElasticsearchAnalysisConfigurer>>> ANALYSIS_CONFIGURER =
private static final OptionalConfigurationProperty<BeanReference<? extends ElasticsearchAnalysisConfigurer>> ANALYSIS_CONFIGURER =
ConfigurationProperty.forKey( SearchBackendElasticsearchSettings.ANALYSIS_CONFIGURER )
.asBeanReference( ElasticsearchAnalysisConfigurer.class )
.build();
Expand Down Expand Up @@ -134,8 +134,7 @@ private ElasticsearchAnalysisDefinitionRegistry getAnalysisDefinitionRegistry(Ev
try {
// Apply the user-provided analysis configurer if necessary
final BeanProvider beanProvider = buildContext.getServiceManager().getBeanProvider();
return ANALYSIS_CONFIGURER.get( propertySource )
.map( beanProvider::getBean )
return ANALYSIS_CONFIGURER.getAndMap( propertySource, beanProvider::getBean )
.map( configurer -> {
ElasticsearchAnalysisDefinitionContainerContextImpl collector
= new ElasticsearchAnalysisDefinitionContainerContextImpl();
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.backend.spi.BackendBuildContext;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.engine.environment.bean.BeanProvider;
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.util.EventContext;
Expand All @@ -53,7 +54,7 @@ public class LuceneBackendFactory implements BackendFactory {
.as( Version.class, LuceneBackendFactory::parseLuceneVersion )
.build();

private static final ConfigurationProperty<Optional<String>> DIRECTORY_PROVIDER =
private static final OptionalConfigurationProperty<String> DIRECTORY_PROVIDER =
ConfigurationProperty.forKey( SearchBackendLuceneSettings.LUCENE_DIRECTORY_PROVIDER )
.asString()
.build();
Expand All @@ -70,7 +71,7 @@ public class LuceneBackendFactory implements BackendFactory {
.withDefault( SearchBackendLuceneSettings.Defaults.MULTI_TENANCY_STRATEGY )
.build();

private static final ConfigurationProperty<Optional<BeanReference<? extends LuceneAnalysisConfigurer>>> ANALYSIS_CONFIGURER =
private static final OptionalConfigurationProperty<BeanReference<? extends LuceneAnalysisConfigurer>> ANALYSIS_CONFIGURER =
ConfigurationProperty.forKey( SearchBackendLuceneSettings.ANALYSIS_CONFIGURER )
.asBeanReference( LuceneAnalysisConfigurer.class )
.build();
Expand Down Expand Up @@ -121,14 +122,10 @@ private Version getLuceneVersion(EventContext backendContext, ConfigurationPrope
}

private DirectoryProvider getDirectoryProvider(EventContext backendContext, ConfigurationPropertySource propertySource) {
// TODO be more clever about the type, also supports providing a class
Optional<String> directoryProviderProperty = DIRECTORY_PROVIDER.get( propertySource );

if ( !directoryProviderProperty.isPresent() ) {
throw log.undefinedLuceneDirectoryProvider( backendContext );
}

String directoryProviderString = directoryProviderProperty.get();
// TODO be more clever about the type, also supports providing a class => use a BeanReference?
String directoryProviderString = DIRECTORY_PROVIDER.getOrThrow(
propertySource, propertyKey -> log.undefinedLuceneDirectoryProvider( propertyKey, backendContext )
);

if ( "local_directory".equals( directoryProviderString ) ) {
// TODO GSM: implement the checks properly
Expand Down Expand Up @@ -164,8 +161,7 @@ private LuceneAnalysisDefinitionRegistry getAnalysisDefinitionRegistry(EventCont
try {
// Apply the user-provided analysis configurer if necessary
final BeanProvider beanProvider = buildContext.getServiceManager().getBeanProvider();
return ANALYSIS_CONFIGURER.get( propertySource )
.map( beanProvider::getBean )
return ANALYSIS_CONFIGURER.getAndMap( propertySource, beanProvider::getBean )
.map( configurer -> {
LuceneAnalysisComponentFactory analysisComponentFactory = new LuceneAnalysisComponentFactory(
luceneVersion,
Expand Down
Expand Up @@ -164,8 +164,8 @@ SearchException unableToCreateRootDirectoryForLocalDirectoryBackend(Path rootDir
@Param EventContext context, @Cause Exception e);

@Message(id = ID_OFFSET_2 + 3,
value = "Undefined Lucene directory provider.")
SearchException undefinedLuceneDirectoryProvider(@Param EventContext context);
value = "Undefined Lucene directory provider for property '%1$s'.")
SearchException undefinedLuceneDirectoryProvider(String propertyKey, @Param EventContext context);

@Message(id = ID_OFFSET_2 + 4,
value = "Unrecognized Lucene directory provider '%1$s'.")
Expand Down
Expand Up @@ -15,23 +15,32 @@
import org.hibernate.search.engine.logging.impl.Log;
import org.hibernate.search.util.impl.common.LoggerFactory;

class FunctionConfigurationProperty<T> implements ConfigurationProperty<T> {
abstract class AbstractConfigurationProperty<T> implements ConfigurationProperty<T> {

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );

private final String key;
private final Function<Optional<?>, T> function;

FunctionConfigurationProperty(String key, Function<Optional<?>, T> function) {
AbstractConfigurationProperty(String key) {
this.key = key;
this.function = function;
}

@Override
public T get(ConfigurationPropertySource source) {
return doGet( source, Function.identity() );
}

@Override
public <R> R getAndTransform(ConfigurationPropertySource source, Function<T, R> transform) {
return doGet( source, transform );
}

abstract <R> R convert(Optional<?> rawValue, Function<T, R> transform);

<R> R doGet(ConfigurationPropertySource source, Function<T, R> transform) {
Optional<?> rawValue = source.get( key );
try {
return function.apply( rawValue );
return convert( rawValue, transform );
}
catch (RuntimeException e) {
String displayedKey = key;
Expand Down
@@ -0,0 +1,29 @@
/*
* 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.cfg.impl;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

final class DefaultedConfigurationProperty<T> extends AbstractConfigurationProperty<T> {

private final Function<Object, Optional<T>> converter;
private final Supplier<T> defaultValueSupplier;

DefaultedConfigurationProperty(String key, Function<Object, Optional<T>> converter, Supplier<T> defaultValueSupplier) {
super( key );
this.converter = converter;
this.defaultValueSupplier = defaultValueSupplier;
}

@Override
<R> R convert(Optional<?> rawValue, Function<T, R> transform) {
T defaultedValue = rawValue.flatMap( converter ).orElseGet( defaultValueSupplier );
return transform.apply( defaultedValue );
}
}
Expand Up @@ -8,21 +8,24 @@

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.PropertyContext;
import org.hibernate.search.engine.cfg.spi.DefaultedPropertyContext;

class DefaultedPropertyContext<T> implements PropertyContext<T> {
final class DefaultedPropertyContextImpl<T> implements DefaultedPropertyContext<T> {
private final String key;
private final Function<Optional<?>, T> converter;
private final Function<Object, Optional<T>> converter;
private final Supplier<T> defaultValueSupplier;

DefaultedPropertyContext(String key, Function<Optional<?>, T> converter) {
DefaultedPropertyContextImpl(String key, Function<Object, Optional<T>> converter, Supplier<T> defaultValueSupplier) {
this.key = key;
this.converter = converter;
this.defaultValueSupplier = defaultValueSupplier;
}

@Override
public ConfigurationProperty<T> build() {
return new FunctionConfigurationProperty<>( key, converter );
return new DefaultedConfigurationProperty<>( key, converter, defaultValueSupplier );
}
}
@@ -0,0 +1,40 @@
/*
* 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.cfg.impl;

import java.util.Optional;
import java.util.function.Function;

import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;

final class OptionalConfigurationPropertyImpl<T> extends AbstractConfigurationProperty<Optional<T>>
implements OptionalConfigurationProperty<T> {

private final Function<Object, Optional<T>> converter;

OptionalConfigurationPropertyImpl(String key, Function<Object, Optional<T>> converter) {
super( key );
this.converter = converter;
}

@Override
public <R> Optional<R> getAndMap(ConfigurationPropertySource source, Function<T, R> transform) {
return getAndTransform( source, optional -> optional.map( transform ) );
}

@Override
public T getOrThrow(ConfigurationPropertySource source, Function<String, RuntimeException> exceptionFunction) {
return get( source ).orElseThrow( () -> exceptionFunction.apply( resolveOrRaw( source ) ) );
}

@Override
<R> R convert(Optional<?> rawValue, Function<Optional<T>, R> transform) {
return transform.apply( rawValue.flatMap( converter ) );
}

}
Expand Up @@ -12,16 +12,16 @@
import java.util.function.Supplier;
import java.util.regex.Pattern;

import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.DefaultedPropertyContext;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.OptionalPropertyContext;
import org.hibernate.search.engine.cfg.spi.PropertyContext;

final class OptionalPropertyContextImpl<T> implements OptionalPropertyContext<T> {

private final String key;
private final Function<Object, Optional<T>> converter;

public OptionalPropertyContextImpl(String key, Function<Object, Optional<T>> converter) {
OptionalPropertyContextImpl(String key, Function<Object, Optional<T>> converter) {
this.key = key;
this.converter = converter;
}
Expand All @@ -35,21 +35,17 @@ public OptionalPropertyContext<List<T>> multivalued(Pattern separatorPattern) {
}

@Override
public PropertyContext<T> withDefault(T defaultValue) {
return new DefaultedPropertyContext<>( key, createOptionalParser().andThen( o -> o.orElse( defaultValue ) ) );
public DefaultedPropertyContext<T> withDefault(T defaultValue) {
return new DefaultedPropertyContextImpl<>( key, converter, () -> defaultValue );
}

@Override
public PropertyContext<T> withDefault(Supplier<T> defaultValueSupplier) {
return new DefaultedPropertyContext<>( key, createOptionalParser().andThen( o -> o.orElseGet( defaultValueSupplier ) ) );
public DefaultedPropertyContext<T> withDefault(Supplier<T> defaultValueSupplier) {
return new DefaultedPropertyContextImpl<>( key, converter, defaultValueSupplier );
}

@Override
public ConfigurationProperty<Optional<T>> build() {
return new FunctionConfigurationProperty<>( key, createOptionalParser() );
}

private Function<Optional<?>, Optional<T>> createOptionalParser() {
return o -> o.flatMap( converter );
public OptionalConfigurationProperty<T> build() {
return new OptionalConfigurationPropertyImpl<>( key, converter );
}
}
Expand Up @@ -7,6 +7,7 @@
package org.hibernate.search.engine.cfg.spi;

import java.util.Optional;
import java.util.function.Function;

import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.impl.KeyContextImpl;
Expand All @@ -21,6 +22,17 @@ public interface ConfigurationProperty<T> {
*/
T get(ConfigurationPropertySource source);

/**
* Get and transform the value of this configuration property.
* <p>
* Any exception occurring during transformation will be wrapped in another exception adding some context,
* such as the {@link #resolveOrRaw(ConfigurationPropertySource) resolved key} for this property.
*
* @param source A configuration source.
* @return The value of this property according to the given source.
*/
<R> R getAndTransform(ConfigurationPropertySource source, Function<T, R> transform);

/**
* Resolve the key for this configuration property
* as registered in the underlying configuration source,
Expand Down
Expand Up @@ -6,6 +6,8 @@
*/
package org.hibernate.search.engine.cfg.spi;

public interface PropertyContext<T> {
public interface DefaultedPropertyContext<T> {

ConfigurationProperty<T> build();

}
@@ -0,0 +1,41 @@
/*
* 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.cfg.spi;

import java.util.Optional;
import java.util.function.Function;

import org.hibernate.search.engine.cfg.ConfigurationPropertySource;

public interface OptionalConfigurationProperty<T> extends ConfigurationProperty<Optional<T>> {

/**
* Get and transform the value of this configuration property.
* <p>
* Similar to calling {@link #getAndTransform(ConfigurationPropertySource, Function)},
* but easier to use, since the transform function is applied to the content of the optional,
* not to the optional itself.
* <p>
* Any exception occurring during transformation will be wrapped in another exception adding some context,
* such as the {@link #resolveOrRaw(ConfigurationPropertySource) resolved key} for this property.
*
* @param source A configuration source.
* @return The value of this property according to the given source.
*/
<R> Optional<R> getAndMap(ConfigurationPropertySource source, Function<T, R> transform);

/**
* Get the value of this configuration property, throwing an exception if the value is not present.
*
* @param source A configuration source.
* @param exceptionFunction A function that will be called with the property key as a parameter
* to create an exception if the value is missing.
* @return The value of this property according to the given source.
*/
T getOrThrow(ConfigurationPropertySource source, Function<String, RuntimeException> exceptionFunction);

}

0 comments on commit 13eac3a

Please sign in to comment.