package org.infinispan.query.backend; import static org.hibernate.search.cfg.Environment.INDEX_MANAGER_IMPL_NAME; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.search.analyzer.definition.spi.LuceneAnalyzerDefinitionProvider; import org.hibernate.search.analyzer.definition.spi.LuceneAnalyzerDefinitionSourceService; import org.hibernate.search.cfg.Environment; import org.hibernate.search.cfg.SearchMapping; import org.hibernate.search.cfg.spi.SearchConfiguration; import org.hibernate.search.cfg.spi.SearchConfigurationBase; import org.hibernate.search.engine.service.classloading.impl.DefaultClassLoaderService; import org.hibernate.search.engine.service.classloading.spi.ClassLoaderService; import org.hibernate.search.engine.service.spi.Service; import org.hibernate.search.engine.spi.SearchMappingHelper; import org.hibernate.search.exception.ErrorHandler; import org.hibernate.search.spi.ErrorHandlerFactory; import org.infinispan.factories.ComponentRegistry; import org.infinispan.hibernate.search.spi.CacheManagerService; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.query.affinity.AffinityErrorHandler; import org.infinispan.query.affinity.AffinityIndexManager; import org.infinispan.query.affinity.AffinityShardIdentifierProvider; import org.infinispan.query.logging.Log; import org.infinispan.util.logging.LogFactory; /** * Class that implements {@link org.hibernate.search.cfg.spi.SearchConfiguration} so that within Infinispan-Query, * there * is * no need for a Hibernate Core configuration object. * * @author Navin Surtani * @author Sanne Grinovero */ public class SearchableCacheConfiguration extends SearchConfigurationBase implements SearchConfiguration { private static final String HSEARCH_PREFIX = "hibernate.search."; private static final String SHARDING_STRATEGY = "sharding_strategy"; private static final Log log = LogFactory.getLog(SearchableCacheConfiguration.class, Log.class); private final Map<String, Class<?>> classes; private final Properties properties; private final SearchMapping searchMapping; private final Map<Class<? extends Service>, Object> providedServices; private final DefaultClassLoaderService classLoaderService = new DefaultClassLoaderService(); //TODO customize this to plug in custom analyzers private final LuceneAnalyzerDefinitionProvider analyzerDefProvider = null; private boolean hasAffinity; public SearchableCacheConfiguration(Class<?>[] classArray, Properties properties, EmbeddedCacheManager uninitializedCacheManager, ComponentRegistry cr) { this.providedServices = initializeProvidedServices(uninitializedCacheManager, cr); if (properties == null) { this.properties = new Properties(); } else { this.properties = augment(properties); } classes = new HashMap<>(); for (Class<?> c : classArray) { String classname = c.getName(); classes.put(classname, c); } if (hasAffinity) { ErrorHandler configuredErrorHandler = ErrorHandlerFactory.createErrorHandler(this); this.properties.put(Environment.ERROR_HANDLER, new AffinityErrorHandler(configuredErrorHandler)); } //deal with programmatic mapping: searchMapping = SearchMappingHelper.extractSearchMapping(this); //if we have a SearchMapping then we can predict at least those entities specified in the mapping //and avoid further SearchFactory rebuilds triggered by new entity discovery during cache events if (searchMapping != null) { Set<Class<?>> mappedEntities = searchMapping.getMappedEntities(); for (Class<?> entity : mappedEntities) { classes.put(entity.getName(), entity); } } } private Map initializeProvidedServices(EmbeddedCacheManager uninitializedCacheManager, ComponentRegistry cr) { //Register the SelfLoopedCacheManagerServiceProvider to allow custom IndexManagers to access the CacheManager final InfinispanLoopbackService loopService = new InfinispanLoopbackService(cr, uninitializedCacheManager); HashMap map = new HashMap(3); map.put(LuceneAnalyzerDefinitionSourceService.class, new LuceneAnalyzerDefinitionsBuilderService(analyzerDefProvider)); map.put(ComponentRegistryService.class, loopService); map.put(CacheManagerService.class, loopService); return Collections.unmodifiableMap(map); } @Override public boolean isDeleteByTermEnforced() { return true; } @Override public Iterator<Class<?>> getClassMappings() { return classes.values().iterator(); } @Override public Class<?> getClassMapping(String name) { return classes.get(name); } @Override public String getProperty(String propertyName) { return properties.getProperty(propertyName); } @Override public Properties getProperties() { return properties; } @Override public ReflectionManager getReflectionManager() { return null; } @Override public SearchMapping getProgrammaticMapping() { return searchMapping; } @Override public Map<Class<? extends Service>, Object> getProvidedServices() { return providedServices; } @Override public boolean isTransactionManagerExpected() { return false; } @Override public boolean isIdProvidedImplicit() { return true; } private Properties augment(Properties origin) { Properties target = new Properties(); for (Entry<Object, Object> entry : origin.entrySet()) { Object key = entry.getKey(); if (key instanceof String && !key.toString().startsWith(HSEARCH_PREFIX)) { key = HSEARCH_PREFIX + key.toString(); } target.put(key, entry.getValue()); if (key.toString().endsWith(INDEX_MANAGER_IMPL_NAME) && entry.getValue().equals(AffinityIndexManager.class.getName())) { target.put(key.toString().replace(INDEX_MANAGER_IMPL_NAME, SHARDING_STRATEGY), AffinityShardIdentifierProvider.class.getName()); hasAffinity = true; } } return target; } @Override public ClassLoaderService getClassLoaderService() { //FIXME wire this up to the ClassLoader configuration of the CacheManager //(and avoid using an .impl class from Hibernate Search) return classLoaderService; } }