package org.infinispan.lucene.impl; import static org.infinispan.lucene.impl.DirectoryImplementor.getAddress; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.infinispan.AdvancedCache; import org.infinispan.context.Flag; import org.infinispan.lucene.FileCacheKey; import org.infinispan.lucene.FileListCacheKey; import org.infinispan.lucene.FileMetadata; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import net.jcip.annotations.GuardedBy; /** * Collects operations on the existing fileList, stored as a Set<String> having key * of type FileListCacheKey(indexName). * * @author Sanne Grinovero * @since 4.1 */ public final class FileListOperations { private static final Log log = LogFactory.getLog(InfinispanIndexOutput.class); private final boolean trace = log.isTraceEnabled(); private final FileListCacheKey fileListCacheKey; private final AdvancedCache<FileListCacheKey, Object> cache; private final String indexName; private final AdvancedCache<FileListCacheKey, FileListCacheValue> cacheNoRetrieve; private final Lock readLock; private final Lock writeLock; private final boolean writeAsync; private final int affinitySegmentId; @SuppressWarnings("unchecked") public FileListOperations(AdvancedCache<?, ?> cache, String indexName, boolean writeAsync, int affinitySegmentId) { this.writeAsync = writeAsync; this.affinitySegmentId = affinitySegmentId; this.cache = (AdvancedCache<FileListCacheKey, Object>) cache; this.cacheNoRetrieve = (AdvancedCache<FileListCacheKey, FileListCacheValue>) cache.withFlags(Flag.IGNORE_RETURN_VALUES); this.indexName = indexName; this.fileListCacheKey = new FileListCacheKey(indexName, affinitySegmentId); ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); readLock = lock.readLock(); writeLock = lock.writeLock(); } /** * Adds a new fileName in the list of files making up this index * @param fileName */ void addFileName(final String fileName) { writeLock.lock(); try { final FileListCacheValue fileList = getFileList(); boolean done = fileList.add(fileName); if (done) { updateFileList(fileList); if (trace) log.trace("Updated file listing: added " + fileName); } } finally { writeLock.unlock(); } } /** * @param fileName * @return the FileMetadata associated with the fileName, or null if the file wasn't found. */ public FileMetadata getFileMetadata(final String fileName) { FileCacheKey key = new FileCacheKey(indexName, fileName, affinitySegmentId); FileMetadata metadata = (FileMetadata) cache.get(key); return metadata; } /** * Optimized implementation to perform both a remove and an add * @param toRemove * @param toAdd */ public void removeAndAdd(final String toRemove, final String toAdd) { writeLock.lock(); try { FileListCacheValue fileList = getFileList(); boolean done = fileList.addAndRemove(toAdd, toRemove); if (done) { updateFileList(fileList); if (trace) { log.trace("Updated file listing: added " + toAdd + " and removed " + toRemove); } } } finally { writeLock.unlock(); } } /** * @return an array containing all names of existing "files" */ public String[] listFilenames() { readLock.lock(); try { return getFileList().toArray(); } finally { readLock.unlock(); } } /** * @param fileName * @return true if there is such a named file in this index */ public boolean fileExists(final String fileName) { readLock.lock(); try { return getFileList().contains(fileName); } finally { readLock.unlock(); } } /** * Deleted a file from the list of files actively part of the index * @param fileName */ public void deleteFileName(final String fileName) { writeLock.lock(); try { FileListCacheValue fileList = getFileList(); boolean done = fileList.remove(fileName); if (done) { updateFileList(fileList); if (trace) log.trace("Updated file listing: removed " + fileName); } } finally { writeLock.unlock(); } } /** * Makes sure the Cache is updated. * @param fileList the new content */ @GuardedBy("writeLock") private void updateFileList(FileListCacheValue fileList) { if (writeAsync) { cacheNoRetrieve.putAsync(fileListCacheKey, fileList); } else { if (trace) { log.tracef("Updating file listing view from %s", getAddress(cacheNoRetrieve)); } cacheNoRetrieve.put(fileListCacheKey, fileList); } } /** * @return the current list of files being part of the index */ @GuardedBy("writeLock") private FileListCacheValue getFileList() { FileListCacheValue fileList = (FileListCacheValue) cache.get(fileListCacheKey); if (fileList == null) { fileList = new FileListCacheValue(); FileListCacheValue prev = (FileListCacheValue) cache.putIfAbsent(fileListCacheKey, fileList); if (prev != null) { fileList = prev; } } if (trace) log.trace("Refreshed file listing view"); return fileList; } }