Skip to content

Commit

Permalink
HSEARCH-3766 Add built-in annotation for applying type/property/routi…
Browse files Browse the repository at this point in the history
…ngKey/marker binders
  • Loading branch information
yrodiere committed Nov 18, 2019
1 parent d213216 commit 04c114e
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 13 deletions.
Expand Up @@ -10,16 +10,24 @@
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.environment.bean.BeanResolver;
import org.hibernate.search.mapper.pojo.bridge.binding.IdentifierBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.MarkerBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.PropertyBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.RoutingKeyBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.TypeBindingContext;
import org.hibernate.search.mapper.pojo.bridge.binding.ValueBindingContext;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.IdentifierBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.MarkerBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.RoutingKeyBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.TypeBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.ValueBinder;

/**
* A binder that upon building retrieves a delegate binder from the bean provider,
* then delegates to that binder.
*/
public final class BeanDelegatingBinder
implements IdentifierBinder, ValueBinder {
implements TypeBinder, PropertyBinder, RoutingKeyBinder, MarkerBinder, IdentifierBinder, ValueBinder {

private final BeanReference<?> delegateReference;

Expand All @@ -32,6 +40,38 @@ public String toString() {
return getClass().getSimpleName() + "[delegateReference=" + delegateReference + "]";
}

@Override
public void bind(TypeBindingContext context) {
try ( BeanHolder<? extends TypeBinder> delegateHolder =
createDelegate( context.getBeanResolver(), TypeBinder.class ) ) {
delegateHolder.get().bind( context );
}
}

@Override
public void bind(PropertyBindingContext context) {
try ( BeanHolder<? extends PropertyBinder> delegateHolder =
createDelegate( context.getBeanResolver(), PropertyBinder.class ) ) {
delegateHolder.get().bind( context );
}
}

@Override
public void bind(RoutingKeyBindingContext context) {
try ( BeanHolder<? extends RoutingKeyBinder> delegateHolder =
createDelegate( context.getBeanResolver(), RoutingKeyBinder.class ) ) {
delegateHolder.get().bind( context );
}
}

@Override
public void bind(MarkerBindingContext context) {
try ( BeanHolder<? extends MarkerBinder> delegateHolder =
createDelegate( context.getBeanResolver(), MarkerBinder.class ) ) {
delegateHolder.get().bind( context );
}
}

@Override
public void bind(IdentifierBindingContext<?> context) {
try ( BeanHolder<? extends IdentifierBinder> delegateHolder =
Expand Down
Expand Up @@ -91,18 +91,8 @@ SearchException unableToResolveDefaultValueBridgeFromSourceType(
@FormatWith(PojoTypeModelFormatter.class) PojoTypeModel<?> sourceType);

@Message(id = ID_OFFSET_2 + 3,
value = "Annotation type '%2$s' is annotated with '%1$s',"
+ " but the binder reference is empty.")
SearchException missingBinderReferenceInBridgeMapping(
@FormatWith(ClassFormatter.class) Class<? extends Annotation> metaAnnotationType,
@FormatWith(ClassFormatter.class) Class<? extends Annotation> annotationType);

@Message(id = ID_OFFSET_2 + 4,
value = "Annotation type '%2$s' is annotated with '%1$s',"
+ " but the binder reference is empty.")
SearchException missingBinderReferenceInMarkerMapping(
@FormatWith(ClassFormatter.class) Class<? extends Annotation> metaAnnotationType,
@FormatWith(ClassFormatter.class) Class<? extends Annotation> annotationType);
value = "The binder reference is empty.")
SearchException missingBinderReferenceInBinding();

@Message(id = ID_OFFSET_2 + 5,
value = "The field annotation defines both valueBridge and valueBinder."
Expand Down
@@ -0,0 +1,44 @@
/*
* 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.mapper.pojo.mapping.definition.annotation;

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

import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.MarkerBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.MarkerBinder;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMapping;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessorRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.impl.MarkerBindingProcessor;

/**
* Applies a marker to a property using a {@link MarkerBinder}.
*/
@Documented
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MarkerBinding.List.class)
@PropertyMapping(processor = @PropertyMappingAnnotationProcessorRef(type = MarkerBindingProcessor.class))
public @interface MarkerBinding {

/**
* @return A reference to the binder to use.
* @see MarkerBinderRef
*/
MarkerBinderRef binder();

@Documented
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
MarkerBinding[] value();
}
}
@@ -0,0 +1,54 @@
/*
* 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.mapper.pojo.mapping.definition.annotation;

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

import org.hibernate.search.mapper.pojo.bridge.PropertyBridge;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMapping;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessorRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.impl.PropertyBindingProcessor;

/**
* Maps a property to index fields using a {@link PropertyBinder},
* which will define a {@link PropertyBridge}.
* <p>
* This is a more complicated,
* but more powerful alternative to mapping properties to field directly
* using field annotations such as {@link GenericField}.
* <p>
* See the reference documentation for more information about bridges in general,
* and property bridges in particular.
*/
@Documented
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(PropertyBinding.List.class)
@PropertyMapping(processor = @PropertyMappingAnnotationProcessorRef(type = PropertyBindingProcessor.class))
public @interface PropertyBinding {

/**
* @return A reference to the binder to use.
* @see PropertyBinderRef
*/
PropertyBinderRef binder();

@Documented
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
PropertyBinding[] value();
}

}
@@ -0,0 +1,53 @@
/*
* 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.mapper.pojo.mapping.definition.annotation;

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

import org.hibernate.search.mapper.pojo.bridge.RoutingKeyBridge;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.RoutingKeyBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.RoutingKeyBinder;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.TypeMapping;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.TypeMappingAnnotationProcessorRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.impl.RoutingKeyBindingProcessor;

/**
* Maps an indexed type to its routing keys using a {@link RoutingKeyBinder},
* which will define a {@link RoutingKeyBridge}.
* <p>
* See the reference documentation for more information about bridges in general,
* and routing key bridges in particular.
*
* @hsearch.experimental This type is under active development.
* Usual compatibility policies do not apply: incompatible changes may be introduced in any future release.
*/
@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RoutingKeyBinding.List.class)
@TypeMapping(processor = @TypeMappingAnnotationProcessorRef(type = RoutingKeyBindingProcessor.class))
public @interface RoutingKeyBinding {

/**
* @return A reference to the binder to use.
* @see RoutingKeyBinderRef
*/
RoutingKeyBinderRef binder();

@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
RoutingKeyBinding[] value();
}

}
@@ -0,0 +1,54 @@
/*
* 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.mapper.pojo.mapping.definition.annotation;

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

import org.hibernate.search.mapper.pojo.bridge.TypeBridge;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.TypeBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.TypeBinder;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.TypeMapping;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.TypeMappingAnnotationProcessorRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.impl.TypeBindingProcessor;

/**
* Maps a type to index fields using a {@link TypeBinder},
* which will define a {@link TypeBridge}.
* <p>
* This is a more complicated,
* but more powerful alternative to mapping properties to field directly
* using field annotations such as {@link GenericField}.
* <p>
* See the reference documentation for more information about bridges in general,
* and type bridges in particular.
*/
@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(TypeBinding.List.class)
@TypeMapping(processor = @TypeMappingAnnotationProcessorRef(type = TypeBindingProcessor.class))
public @interface TypeBinding {

/**
* @return A reference to the binder to use.
* @see TypeBinderRef
*/
TypeBinderRef binder();

@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
TypeBinding[] value();
}

}
@@ -0,0 +1,48 @@
/*
* 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.mapper.pojo.mapping.definition.annotation.processing.impl;

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

import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.MarkerBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.impl.BeanDelegatingBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.MarkerBinder;
import org.hibernate.search.mapper.pojo.logging.impl.Log;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.MarkerBinding;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.MappingAnnotationProcessorContext;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessor;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessorContext;
import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.PropertyMappingStep;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

public final class MarkerBindingProcessor implements PropertyMappingAnnotationProcessor<MarkerBinding> {

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

@Override
public void process(PropertyMappingStep mapping, MarkerBinding annotation,
PropertyMappingAnnotationProcessorContext context) {
MarkerBinder binder = createBinder( annotation.binder(), context );
mapping.marker( binder );
}

private MarkerBinder createBinder(MarkerBinderRef binderReferenceAnnotation, MappingAnnotationProcessorContext context) {
Optional<BeanReference<? extends MarkerBinder>> binderReference = context.toBeanReference(
MarkerBinder.class,
MarkerBinderRef.UndefinedBinderImplementationType.class,
binderReferenceAnnotation.type(), binderReferenceAnnotation.name()
);

if ( !binderReference.isPresent() ) {
throw log.missingBinderReferenceInBinding();
}

return new BeanDelegatingBinder( binderReference.get() );
}
}
@@ -0,0 +1,48 @@
/*
* 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.mapper.pojo.mapping.definition.annotation.processing.impl;

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

import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.impl.BeanDelegatingBinder;
import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
import org.hibernate.search.mapper.pojo.logging.impl.Log;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyBinding;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.MappingAnnotationProcessorContext;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessor;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.processing.PropertyMappingAnnotationProcessorContext;
import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.PropertyMappingStep;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

public final class PropertyBindingProcessor implements PropertyMappingAnnotationProcessor<PropertyBinding> {

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

@Override
public void process(PropertyMappingStep mapping, PropertyBinding annotation,
PropertyMappingAnnotationProcessorContext context) {
PropertyBinder binder = createBinder( annotation.binder(), context );
mapping.binder( binder );
}

private PropertyBinder createBinder(PropertyBinderRef binderReferenceAnnotation, MappingAnnotationProcessorContext context) {
Optional<BeanReference<? extends PropertyBinder>> binderReference = context.toBeanReference(
PropertyBinder.class,
PropertyBinderRef.UndefinedBinderImplementationType.class,
binderReferenceAnnotation.type(), binderReferenceAnnotation.name()
);

if ( !binderReference.isPresent() ) {
throw log.missingBinderReferenceInBinding();
}

return new BeanDelegatingBinder( binderReference.get() );
}
}

0 comments on commit 04c114e

Please sign in to comment.