/* * 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.store.impl; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; import java.util.Properties; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.LockFactory; import org.apache.lucene.store.MMapDirectory; import org.apache.lucene.store.NIOFSDirectory; import org.apache.lucene.store.SimpleFSDirectory; import org.hibernate.search.engine.service.spi.ServiceManager; import org.hibernate.search.exception.SearchException; import org.hibernate.search.store.spi.DirectoryHelper; import org.hibernate.search.store.spi.LockFactoryCreator; import org.hibernate.search.util.StringHelper; import org.hibernate.search.util.configuration.impl.ConfigurationParseHelper; import org.hibernate.search.util.impl.FileHelper; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; /** * @author Emmanuel Bernard * @author Sanne Grinovero * @author Hardy Ferentschik */ public final class DirectoryProviderHelper { private static final Log log = LoggerFactory.make(); private static final String ROOT_INDEX_PROP_NAME = "sourceBase"; private static final String RELATIVE_INDEX_PROP_NAME = "source"; private static final String COPY_BUFFER_SIZE_PROP_NAME = "buffer_size_on_copy"; private static final String FS_DIRECTORY_TYPE_PROP_NAME = "filesystem_access_type"; private static final String REFRESH_PROP_NAME = "refresh"; private static final String RETRY_INITIALIZE_PROP_NAME = "retry_initialize_period"; private DirectoryProviderHelper() { } /** * @deprecated Use getSourceDirectoryPath * @param indexName the name of the index (directory) to create * @param properties the configuration properties * @param needWritePermissions when true the directory will be tested for read-write permissions. * @return The file representing the source directory */ @Deprecated public static File getSourceDirectory(String indexName, Properties properties, boolean needWritePermissions) { return getSourceDirectoryPath( indexName, properties, needWritePermissions ).toFile(); } /** * Build a directory name out of a root and relative path, guessing the significant part * and checking for the file availability * * @param indexName the name of the index (directory) to create * @param properties the configuration properties * @param needWritePermissions when true the directory will be tested for read-write permissions. * @return The file representing the source directory */ public static Path getSourceDirectoryPath(String indexName, Properties properties, boolean needWritePermissions) { String root = properties.getProperty( ROOT_INDEX_PROP_NAME ); String relative = properties.getProperty( RELATIVE_INDEX_PROP_NAME ); Path sourceDirectory; if ( log.isTraceEnabled() ) { log.trace( "Guess source directory from " + ROOT_INDEX_PROP_NAME + " " + ( root != null ? root : "<null>" ) + " and " + RELATIVE_INDEX_PROP_NAME + " " + ( relative != null ? relative : "<null>" ) ); } if ( relative == null ) { relative = indexName; } if ( StringHelper.isEmpty( root ) ) { log.debug( "No root directory, go with relative " + relative ); sourceDirectory = FileSystems.getDefault().getPath( relative ); if ( Files.notExists( sourceDirectory ) ) { throw log.sourceDirectoryNotExisting( relative ); } else if ( ! Files.isReadable( sourceDirectory ) ) { throw log.directoryIsNotReadable( relative ); } else if ( ! Files.isDirectory( sourceDirectory ) ) { throw log.fileIsADirectory( relative ); } //else keep source as it } else { Path rootDir = FileSystems.getDefault().getPath( root ); makeSanityCheckedDirectory( rootDir, indexName, needWritePermissions ); sourceDirectory = rootDir.resolve( relative ); makeSanityCheckedDirectory( sourceDirectory, indexName, needWritePermissions ); log.debug( "Got directory from root + relative" ); } return sourceDirectory; } /** * Creates an FSDirectory in provided directory and initializes * an index if not already existing. * * @param indexDir the directory where to write a new index * @param properties the configuration properties * @param serviceManager provides access to services * @return the created {@code FSDirectory} instance * @throws java.io.IOException if an error */ public static FSDirectory createFSIndex(File indexDir, Properties properties, ServiceManager serviceManager) throws IOException { LockFactory lockFactory = getLockFactory( indexDir, properties, serviceManager ); FSDirectoryType fsDirectoryType = FSDirectoryType.getType( properties ); FSDirectory fsDirectory = fsDirectoryType.getDirectory( indexDir.toPath(), lockFactory ); log.debugf( "Initialize index: '%s'", indexDir.getAbsolutePath() ); DirectoryHelper.initializeIndexIfNeeded( fsDirectory ); return fsDirectory; } private static LockFactory getLockFactory(File indexDir, Properties properties, ServiceManager serviceManager) { try { return serviceManager.requestService( LockFactoryCreator.class ).createLockFactory( indexDir, properties ); } finally { serviceManager.releaseService( LockFactoryCreator.class ); } } /** * @param directory The directory to create or verify * @param indexName To label exceptions * @param verifyIsWritable Verify the directory is writable * @throws SearchException if the index cannot be created, it's not a directory or it's not writeable */ public static void makeSanityCheckedDirectory(Path directory, String indexName, boolean verifyIsWritable) { if ( Files.notExists( directory ) ) { log.indexDirectoryNotFoundCreatingNewOne( directory.toString() ); //if not existing, create the full path try { Files.createDirectories( directory ); } catch (IOException e) { throw new SearchException( "Unable to create index directory: " + directory + " for index " + indexName ); } } else { // else check it is not a file if ( ! Files.isDirectory( directory ) ) { throw new SearchException( "Unable to initialize index: " + indexName + ": " + directory + " is a file." ); } } // and ensure it's writable if ( verifyIsWritable && ( ! Files.isWritable( directory ) ) ) { throw new SearchException( "Cannot write into index directory: " + directory + " for index " + indexName ); } } /** * @deprecated Use makeSanityCheckedDirectory(Path directory, String indexName, boolean verifyIsWritable) * * @param directory The directory to create or verify * @param indexName To label exceptions * @param verifyIsWritable Verify the directory is writable */ @Deprecated public static void makeSanityCheckedDirectory(File directory, String indexName, boolean verifyIsWritable) { makeSanityCheckedDirectory( directory.toPath(), indexName, verifyIsWritable ); } /** * @param properties the configuration of the DirectoryProvider * @param directoryProviderName the name of the DirectoryProvider, used for error reporting * @return The period in milliseconds to keep retrying initialization of a DirectoryProvider */ static long getRetryInitializePeriod(Properties properties, String directoryProviderName) { int retry_period_seconds = ConfigurationParseHelper.getIntValue( properties, RETRY_INITIALIZE_PROP_NAME, 0 ); log.debugf( "Retry initialize period for Directory %s: %d seconds", directoryProviderName, retry_period_seconds ); if ( retry_period_seconds < 0 ) { throw new SearchException( RETRY_INITIALIZE_PROP_NAME + " for Directory " + directoryProviderName + " must be a positive integer" ); } return retry_period_seconds * 1000; //convert into milliseconds } static long getRefreshPeriod(Properties properties, String directoryProviderName) { String refreshPeriod = properties.getProperty( REFRESH_PROP_NAME, "3600" ); long period; try { period = Long.parseLong( refreshPeriod ); } catch (NumberFormatException nfe) { throw new SearchException( "Unable to initialize index: " + directoryProviderName + "; refresh period is not numeric.", nfe ); } log.debugf( "Refresh period: %d seconds", (Long) period ); return period * 1000; //per second } /** * Users may configure the number of MB to use as * "chunk size" for large file copy operations performed * by DirectoryProviders. * * @param indexName the index name * @param properties the configuration properties * @return the number of Bytes to use as "chunk size" in file copy operations. */ public static long getCopyBufferSize(String indexName, Properties properties) { String value = properties.getProperty( COPY_BUFFER_SIZE_PROP_NAME ); long size = FileHelper.DEFAULT_COPY_BUFFER_SIZE; if ( value != null ) { try { size = Long.parseLong( value ) * 1024 * 1024; //from MB to B. } catch (NumberFormatException nfe) { throw new SearchException( "Unable to initialize index " + indexName + "; " + COPY_BUFFER_SIZE_PROP_NAME + " is not numeric.", nfe ); } if ( size <= 0 ) { throw new SearchException( "Unable to initialize index " + indexName + "; " + COPY_BUFFER_SIZE_PROP_NAME + " needs to be greater than zero." ); } } return size; } private enum FSDirectoryType { AUTO( null ), SIMPLE( SimpleFSDirectory.class ), NIO( NIOFSDirectory.class ), MMAP( MMapDirectory.class ); private Class<? extends FSDirectory> fsDirectoryClass; FSDirectoryType(Class<? extends FSDirectory> fsDirectoryClass) { this.fsDirectoryClass = fsDirectoryClass; } public FSDirectory getDirectory(Path indexDir, LockFactory factory) throws IOException { FSDirectory directory; if ( fsDirectoryClass == null ) { directory = FSDirectory.open( indexDir, factory ); } else { try { Constructor<? extends FSDirectory> constructor = fsDirectoryClass.getConstructor( Path.class, LockFactory.class ); directory = constructor.newInstance( indexDir, factory ); } catch (NoSuchMethodException e) { throw new SearchException( "Unable to find appropriate FSDirectory constructor", e ); } catch (InstantiationException e) { throw new SearchException( "Unable to instantiate FSDirectory class " + fsDirectoryClass.getName(), e ); } catch (IllegalAccessException e) { throw new SearchException( "Unable to instantiate FSDirectory class " + fsDirectoryClass.getName(), e ); } catch (InvocationTargetException e) { throw new SearchException( "Unable to instantiate FSDirectory class " + fsDirectoryClass.getName(), e ); } } return directory; } public static FSDirectoryType getType(Properties properties) { FSDirectoryType fsDirectoryType; String fsDirectoryTypeValue = properties.getProperty( FS_DIRECTORY_TYPE_PROP_NAME ); if ( StringHelper.isNotEmpty( fsDirectoryTypeValue ) ) { try { fsDirectoryType = Enum.valueOf( FSDirectoryType.class, fsDirectoryTypeValue.toUpperCase( Locale.ROOT ) ); } catch (IllegalArgumentException e) { throw new SearchException( "Invalid option value for " + FS_DIRECTORY_TYPE_PROP_NAME + ": " + fsDirectoryTypeValue ); } } else { fsDirectoryType = AUTO; } return fsDirectoryType; } } }