Skip to content

Commit

Permalink
HSEARCH-3101 Add a way to define named beans from integrations (backe…
Browse files Browse the repository at this point in the history
…nds, ...)
  • Loading branch information
yrodiere committed Dec 4, 2018
1 parent b71608d commit f001ea4
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 5 deletions.
Expand Up @@ -156,7 +156,7 @@ public SearchIntegration build() {
beanResolver = new ReflectionBeanResolver( classResolver );
}

BeanProvider beanProvider = new BeanProviderImpl( beanResolver );
BeanProvider beanProvider = new BeanProviderImpl( classResolver, beanResolver );
ServiceManager serviceManager = new ServiceManagerImpl( classResolver, resourceResolver, beanProvider );
RootBuildContext rootBuildContext = new RootBuildContext( serviceManager, failureCollector );

Expand Down
@@ -0,0 +1,33 @@
/*
* 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.environment.bean.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.hibernate.search.engine.environment.bean.spi.BeanConfigurationContext;
import org.hibernate.search.engine.environment.bean.spi.BeanFactory;
import org.hibernate.search.util.impl.common.Contracts;

final class BeanConfigurationContextImpl implements BeanConfigurationContext {

private Map<ConfiguredBeanKey<?>, BeanFactory<?>> explicitlyConfiguredBeans = new HashMap<>();

@Override
public <T> void define(Class<T> exposedType, String name, BeanFactory<T> factory) {
Contracts.assertNotNull( exposedType, "exposedType" );
Contracts.assertNotNull( name, "name" );
Contracts.assertNotNull( factory, "factory" );
explicitlyConfiguredBeans.put( new ConfiguredBeanKey<>( exposedType, name ), factory );
}

Map<ConfiguredBeanKey<?>, BeanFactory<?>> getConfiguredBeans() {
return Collections.unmodifiableMap( explicitlyConfiguredBeans );
}

}
@@ -0,0 +1,23 @@
/*
* 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.environment.bean.impl;

import org.hibernate.search.engine.environment.bean.BeanProvider;
import org.hibernate.search.engine.environment.bean.spi.BeanCreationContext;

final class BeanCreationContextImpl implements BeanCreationContext {
private final BeanProvider beanProvider;

BeanCreationContextImpl(BeanProvider beanProvider) {
this.beanProvider = beanProvider;
}

@Override
public BeanProvider getBeanProvider() {
return beanProvider;
}
}
Expand Up @@ -7,22 +7,40 @@
package org.hibernate.search.engine.environment.bean.impl;

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

import org.hibernate.search.engine.environment.bean.BeanProvider;
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.environment.bean.spi.BeanConfigurer;
import org.hibernate.search.engine.environment.bean.spi.BeanCreationContext;
import org.hibernate.search.engine.environment.bean.spi.BeanFactory;
import org.hibernate.search.engine.environment.bean.spi.BeanResolver;
import org.hibernate.search.engine.environment.classpath.spi.ClassResolver;
import org.hibernate.search.engine.logging.impl.Log;
import org.hibernate.search.util.SearchException;
import org.hibernate.search.util.impl.common.LoggerFactory;
import org.hibernate.search.util.impl.common.StringHelper;

public class BeanProviderImpl implements BeanProvider {
public final class BeanProviderImpl implements BeanProvider {

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

private final BeanResolver beanResolver;
private final Map<ConfiguredBeanKey<?>, BeanFactory<?>> explicitlyConfiguredBeans;

public BeanProviderImpl(BeanResolver beanResolver) {
private final BeanCreationContext beanCreationContext;

public BeanProviderImpl(ClassResolver classResolver, BeanResolver beanResolver) {
this.beanResolver = beanResolver;

// TODO maybe also add a way to pass configurer through a configuration property?
BeanConfigurationContextImpl configurationContext = new BeanConfigurationContextImpl();
for ( BeanConfigurer beanConfigurer : classResolver.loadJavaServices( BeanConfigurer.class ) ) {
beanConfigurer.configure( configurationContext );
}
this.explicitlyConfiguredBeans = configurationContext.getConfiguredBeans();

this.beanCreationContext = new BeanCreationContextImpl( this );
}

@Override
Expand All @@ -38,7 +56,7 @@ public <T> T getBean(Class<T> expectedClass, String nameReference) {
if ( StringHelper.isEmpty( nameReference ) ) {
throw log.emptyBeanReferenceNameNullOrEmpty();
}
return beanResolver.resolve( expectedClass, nameReference );
return getBeanFromBeanResolverOrConfiguredBeans( expectedClass, nameReference );
}

@Override
Expand All @@ -56,7 +74,8 @@ public <T> T getBean(Class<T> expectedClass, BeanReference reference) {
return beanResolver.resolve( expectedClass, nameReference, typeReference );
}
else if ( nameProvided ) {
return beanResolver.resolve( expectedClass, nameReference );
// This is the only situation where querying configured beans make sense
return getBeanFromBeanResolverOrConfiguredBeans( expectedClass, nameReference );
}
else if ( typeProvided ) {
return beanResolver.resolve( expectedClass, typeReference );
Expand All @@ -65,4 +84,41 @@ else if ( typeProvided ) {
throw log.emptyBeanReferenceNoNameNoType();
}
}

private <T> T getBeanFromBeanResolverOrConfiguredBeans(Class<T> expectedClass, String nameReference) {
try {
return beanResolver.resolve( expectedClass, nameReference );
}
catch (SearchException e) {
/*
* Fall back to an explicitly configured bean.
* It's important to do this *after* trying the bean resolver,
* so that adding explicitly configured beans in a new version of Hibernate Search
* doesn't break existing user's configuration.
*/
try {
T explicitlyConfiguredBean = getExplicitlyConfiguredBean( expectedClass, nameReference );
if ( explicitlyConfiguredBean != null ) {
return explicitlyConfiguredBean;
}
}
catch (RuntimeException e2) {
e.addSuppressed( e2 );
}
throw e;
}
}

private <T> T getExplicitlyConfiguredBean(Class<T> exposedType, String name) {
ConfiguredBeanKey<T> key = new ConfiguredBeanKey<>( exposedType, name );
@SuppressWarnings("unchecked") // We know the factory has the correct type, see BeanConfigurationContextImpl
BeanFactory<T> factory = (BeanFactory<T>) explicitlyConfiguredBeans.get( key );
if ( factory == null ) {
return null;
}
else {
return factory.create( beanCreationContext );
}
}

}
@@ -0,0 +1,34 @@
/*
* 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.environment.bean.impl;

import java.util.Objects;

final class ConfiguredBeanKey<T> {
private final Class<T> exposedType;
private final String name;

ConfiguredBeanKey(Class<T> exposedType, String name) {
this.exposedType = exposedType;
this.name = name;
}

@Override
public boolean equals(Object obj) {
if ( obj == null || getClass() != obj.getClass() ) {
return false;
}
ConfiguredBeanKey<?> other = (ConfiguredBeanKey<?>) obj;
return Objects.equals( exposedType, other.exposedType )
&& Objects.equals( name, other.name );
}

@Override
public int hashCode() {
return Objects.hash( exposedType, name );
}
}
@@ -0,0 +1,13 @@
/*
* 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.environment.bean.spi;

public interface BeanConfigurationContext {

<T> void define(Class<T> exposedType, String name, BeanFactory<T> factory);

}
@@ -0,0 +1,17 @@
/*
* 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.environment.bean.spi;

public interface BeanConfigurer {

/**
* Configure beans as necessary using the given {@code context}.
* @param context A context exposing methods to configure beans.
*/
void configure(BeanConfigurationContext context);

}
@@ -0,0 +1,15 @@
/*
* 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.environment.bean.spi;

import org.hibernate.search.engine.environment.bean.BeanProvider;

public interface BeanCreationContext {

BeanProvider getBeanProvider();

}
@@ -0,0 +1,13 @@
/*
* 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.environment.bean.spi;

public interface BeanFactory<T> {

T create(BeanCreationContext context);

}

0 comments on commit f001ea4

Please sign in to comment.