package org.infinispan.lucene.impl; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.infinispan.atomic.DeltaAware; import org.infinispan.commons.io.UnsignedNumeric; import org.infinispan.commons.marshall.AbstractExternalizer; import org.infinispan.lucene.ExternalizerIds; import net.jcip.annotations.ThreadSafe; /** * Maintains a Set of filenames contained in the index. Does not implement Set for simplicity, and does internal locking * to provide a safe Externalizer. * * @author Sanne Grinovero * @since 7.0 */ @ThreadSafe public final class FileListCacheValue implements DeltaAware { private final Set<String> filenames = new HashSet<>(); private FileListCacheValueDelta fileListValueDelta = new FileListCacheValueDelta(); private final Lock writeLock; private final Lock readLock; /** * Constructs a new empty set of filenames */ public FileListCacheValue() { ReadWriteLock namesLock = new ReentrantReadWriteLock(); writeLock = namesLock.writeLock(); readLock = namesLock.readLock(); } /** * Initializes a new instance storing the passed values. * @param listAll the strings to store. */ public FileListCacheValue(String[] listAll) { this(); Collections.addAll(filenames, listAll); } protected void apply(List<Operation> operations) { writeLock.lock(); try { for (Operation operation : operations) { operation.apply(filenames); } } finally { writeLock.unlock(); } } /** * Removes the filename from the set if it exists * @param fileName * @return true if the set was mutated */ public boolean remove(String fileName) { writeLock.lock(); try { boolean removed = filenames.remove(fileName); if (removed) { fileListValueDelta.removeOperation(fileName); } return removed; } finally { writeLock.unlock(); } } /** * Adds the filename from the set if it exists * @param fileName * @return true if the set was mutated */ public boolean add(String fileName) { writeLock.lock(); try { boolean added = filenames.add(fileName); if (added) { fileListValueDelta.addOperation(fileName); } return added; } finally { writeLock.unlock(); } } public boolean addAndRemove(String toAdd, String toRemove) { writeLock.lock(); try { boolean doneAdd = filenames.add(toAdd); boolean doneRemove = filenames.remove(toRemove); if (doneAdd) { fileListValueDelta.addOperation(toAdd); } if (doneRemove) { fileListValueDelta.removeOperation(toRemove); } return doneAdd || doneRemove; } finally { writeLock.unlock(); } } public String[] toArray() { readLock.lock(); try { return filenames.toArray(new String[filenames.size()]); } finally { readLock.unlock(); } } public boolean contains(String fileName) { readLock.lock(); try { return filenames.contains(fileName); } finally { readLock.unlock(); } } @Override public int hashCode() { readLock.lock(); try { return filenames.hashCode(); } finally { readLock.unlock(); } } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (FileListCacheValue.class != obj.getClass()) return false; final FileListCacheValue other = (FileListCacheValue) obj; final HashSet<String> copyFromOther; other.readLock.lock(); try { copyFromOther = new HashSet<>(other.filenames); } finally { other.readLock.unlock(); } readLock.lock(); try { return (filenames.equals(copyFromOther)); } finally { readLock.unlock(); } } @Override public String toString() { readLock.lock(); try { return "FileListCacheValue [filenames=" + filenames + "]"; } finally { readLock.unlock(); } } @Override public FileListCacheValueDelta delta() { readLock.lock(); try { FileListCacheValueDelta toReturn = fileListValueDelta; fileListValueDelta = new FileListCacheValueDelta(); return toReturn; } finally { readLock.unlock(); } } @Override public void commit() { fileListValueDelta.discardOps(); } public static final class Externalizer extends AbstractExternalizer<FileListCacheValue> { @Override public void writeObject(final ObjectOutput output, final FileListCacheValue key) throws IOException { key.readLock.lock(); try { UnsignedNumeric.writeUnsignedInt(output, key.filenames.size()); for (String name : key.filenames) { output.writeUTF(name); } } finally { key.readLock.unlock(); } } @Override public FileListCacheValue readObject(final ObjectInput input) throws IOException { int size = UnsignedNumeric.readUnsignedInt(input); String[] names = new String[size]; for (int i = 0; i < size; i++) { names[i] = input.readUTF(); } return new FileListCacheValue(names); } @Override public Integer getId() { return ExternalizerIds.FILE_LIST_CACHE_VALUE; } @Override public Set<Class<? extends FileListCacheValue>> getTypeClasses() { return Collections.singleton(FileListCacheValue.class); } } }