package org.infinispan.hibernate.search.spi; import java.io.File; import java.io.IOException; import java.util.Properties; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockFactory; import org.hibernate.search.backend.BackendFactory; import org.hibernate.search.cfg.Environment; import org.hibernate.search.engine.service.spi.ServiceManager; import org.hibernate.search.indexes.spi.DirectoryBasedIndexManager; import org.hibernate.search.spi.BuildContext; import org.hibernate.search.store.spi.DirectoryHelper; import org.hibernate.search.store.spi.LockFactoryCreator; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.hibernate.search.impl.AsyncDeleteExecutorService; import org.infinispan.hibernate.search.impl.LoggerFactory; import org.infinispan.hibernate.search.logging.Log; import org.infinispan.hibernate.search.util.configuration.impl.ConfigurationParseHelper; import org.infinispan.lucene.FileCacheKey; import org.infinispan.lucene.directory.DirectoryBuilder; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.remoting.transport.Address; import org.infinispan.util.concurrent.WithinThreadExecutor; /** * A DirectoryProvider using Infinispan to store the Index. This depends on the CacheManagerServiceProvider to get a * reference to the Infinispan {@link EmbeddedCacheManager}. * * @author Sanne Grinovero */ public class InfinispanDirectoryProvider implements org.hibernate.search.store.DirectoryProvider<Directory> { private static final Log log = LoggerFactory.make(); private ServiceManager serviceManager; private String directoryProviderName; private String metadataCacheName; private String dataCacheName; private String lockingCacheName; private Integer chunkSize; private Directory directory; private EmbeddedCacheManager cacheManager; private AsyncDeleteExecutorService deletesExecutor; private boolean writeFileListAsync; private LockFactory indexWriterLockFactory; private int affinityId; private boolean isAsync; public InfinispanDirectoryProvider(int affinityId) { this.affinityId = affinityId; } public InfinispanDirectoryProvider() { this.affinityId = -1; } @Override public void initialize(String directoryProviderName, Properties properties, BuildContext context) { this.directoryProviderName = directoryProviderName; this.serviceManager = context.getServiceManager(); this.cacheManager = serviceManager.requestService(CacheManagerService.class).getEmbeddedCacheManager(); metadataCacheName = InfinispanIntegration.getMetadataCacheName(properties); dataCacheName = InfinispanIntegration.getDataCacheName(properties); lockingCacheName = InfinispanIntegration.getLockingCacheName(properties); //Let it return null if it's not set, so that we can avoid applying any override. chunkSize = ConfigurationParseHelper.getIntValue(properties, "chunk_size"); writeFileListAsync = getWriteFileListAsync(properties); //Only override the default Infinispan LockDirectory if an explicit option is set: if (configurationExplicitlySetsLockFactory(properties)) { File verifiedIndexDir = null; if (isNativeLockingStrategy(properties)) { verifiedIndexDir = DirectoryHelper.getVerifiedIndexDir( directoryProviderName, properties, true ); } indexWriterLockFactory = getLockFactory(verifiedIndexDir, properties); } this.isAsync = !BackendFactory.isConfiguredAsSync(properties); } private LockFactory getLockFactory(File indexDir, Properties properties) { try { return serviceManager.requestService(LockFactoryCreator.class).createLockFactory(indexDir, properties); } finally { serviceManager.releaseService(LockFactoryCreator.class); } } private boolean getWriteFileListAsync(Properties properties) { return ConfigurationParseHelper.getBooleanValue( properties, InfinispanIntegration.WRITE_METADATA_ASYNC, false ); } /** * @param dirConfiguration the properties representing the configuration for this index * @return {@code true} if the configuration contains an override for the locking_strategy */ private boolean configurationExplicitlySetsLockFactory(Properties dirConfiguration) { return dirConfiguration.getProperty(Environment.LOCKING_STRATEGY) != null; } private boolean isNativeLockingStrategy(Properties dirConfiguration) { return "native".equals(dirConfiguration.getProperty(Environment.LOCKING_STRATEGY)); } @Override public void start(DirectoryBasedIndexManager indexManager) { log.debug("Starting InfinispanDirectory"); deletesExecutor = getDeleteOperationsExecutor(); validateCacheManagerConfiguration(); cacheManager.startCaches(metadataCacheName, dataCacheName, lockingCacheName); Cache<?, ?> metadataCache = cacheManager.getCache(metadataCacheName); Cache<?, ?> dataCache = cacheManager.getCache(dataCacheName); Cache<?, ?> lockingCache = cacheManager.getCache(lockingCacheName); org.infinispan.lucene.directory.BuildContext directoryBuildContext = DirectoryBuilder .newDirectoryInstance(metadataCache, dataCache, lockingCache, directoryProviderName) .writeFileListAsynchronously(writeFileListAsync) .deleteOperationsExecutor(isAsync ? new WithinThreadExecutor() : deletesExecutor.getExecutor()); if (chunkSize != null) { directoryBuildContext.chunkSize(chunkSize); } if (indexWriterLockFactory != null) { directoryBuildContext.overrideWriteLocker(indexWriterLockFactory); } if (affinityId >= 0) { directoryBuildContext.affinityLocationIntoSegment(affinityId); } directory = directoryBuildContext.create(); DirectoryHelper.initializeIndexIfNeeded(directory); log.debugf("Initialized Infinispan index: '%s'", directoryProviderName); } private void validateCacheManagerConfiguration() { if (cacheManager.getCacheConfiguration(metadataCacheName) == null) { // Synchronizes on the class instead of instance, since multiple caches may have the provider // and only 1 can define this cache synchronized (InfinispanDirectoryProvider.class) { if (cacheManager.getCacheConfiguration(metadataCacheName) == null) { log.missingIndexCacheConfiguration(metadataCacheName); ConfigurationBuilder builder = new ConfigurationBuilder(); if (cacheManager.getCacheManagerConfiguration().isClustered()) { // Clustered Metadata cache configuration builder .clustering().cacheMode(CacheMode.REPL_SYNC).remoteTimeout(25000) .stateTransfer().awaitInitialTransfer(true).timeout(480000) .locking().useLockStriping(false).lockAcquisitionTimeout(10000).concurrencyLevel(500) ; } else { builder.simpleCache(true); } cacheManager.defineConfiguration(metadataCacheName, builder.build()); } } } if (cacheManager.getCacheConfiguration(dataCacheName) == null) { synchronized (InfinispanDirectoryProvider.class) { if (cacheManager.getCacheConfiguration(dataCacheName) == null) { log.missingIndexCacheConfiguration(dataCacheName); ConfigurationBuilder builder = new ConfigurationBuilder(); if (cacheManager.getCacheManagerConfiguration().isClustered()) { // Clustered Metadata cache configuration builder .clustering().cacheMode(CacheMode.DIST_SYNC).remoteTimeout(25000) .stateTransfer().awaitInitialTransfer(true).timeout(480000) .locking().useLockStriping(false).lockAcquisitionTimeout(10000).concurrencyLevel(500) ; } else { builder.simpleCache(true); } cacheManager.defineConfiguration(dataCacheName, builder.build()); } } } if (cacheManager.getCacheConfiguration(lockingCacheName) == null) { synchronized (InfinispanDirectoryProvider.class) { if (cacheManager.getCacheConfiguration(lockingCacheName) == null) { log.missingIndexCacheConfiguration(lockingCacheName); ConfigurationBuilder builder = new ConfigurationBuilder(); if (cacheManager.getCacheManagerConfiguration().isClustered()) { // Clustered Metadata cache configuration builder .clustering().cacheMode(CacheMode.REPL_SYNC).remoteTimeout(25000) .stateTransfer().awaitInitialTransfer(true).timeout(480000) .locking().useLockStriping(false).lockAcquisitionTimeout(10000).concurrencyLevel(500) ; } else { builder.simpleCache(true); } cacheManager.defineConfiguration(lockingCacheName, builder.build()); } } } } private AsyncDeleteExecutorService getDeleteOperationsExecutor() { return serviceManager.requestService(AsyncDeleteExecutorService.class); } public int pendingDeleteTasks() { return isAsync ? 0 : deletesExecutor.getActiveTasks(); } @Override public void stop() { deletesExecutor.closeAndFlush(); serviceManager.releaseService(AsyncDeleteExecutorService.class); try { directory.close(); } catch (IOException e) { log.unableToCloseLuceneDirectory(directory, e); } serviceManager.releaseService(CacheManagerService.class); log.debug("Stopped InfinispanDirectory"); } @Override public Directory getDirectory() { return directory; } public EmbeddedCacheManager getCacheManager() { return cacheManager; } public Address getLockOwner(String indexName, int affinityId, String lockName) { FileCacheKey fileCacheKey = new FileCacheKey(indexName, lockName, affinityId); Cache<?, Address> lockCache = cacheManager.getCache(lockingCacheName); Address address = lockCache.get(fileCacheKey); log.debugf("Lock owner for %s: %s", fileCacheKey, address); return address; } }