package org.infinispan.stream.impl.local;
import java.util.BitSet;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.cache.impl.AbstractDelegatingCache;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.compat.TypeConverter;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.context.Flag;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.stream.impl.RemovableCloseableIterator;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Stream supplier that is to be used when the underlying stream is composed by {@link CacheEntry} instances. This
* supplier will do the proper filtering by key based on the CacheEntry key.
*/
public class EntryStreamSupplier<K, V> implements AbstractLocalCacheStream.StreamSupplier<CacheEntry<K, V>, Stream<CacheEntry<K, V>>> {
private static final Log log = LogFactory.getLog(EntryStreamSupplier.class);
private static final boolean trace = log.isTraceEnabled();
private final Cache<K, V> cache;
private final ConsistentHash hash;
private final Supplier<Stream<CacheEntry<K, V>>> supplier;
public EntryStreamSupplier(Cache<K, V> cache, ConsistentHash hash, Supplier<Stream<CacheEntry<K, V>>> supplier) {
this.cache = cache;
this.hash = hash;
this.supplier = supplier;
}
@Override
public Stream<CacheEntry<K, V>> buildStream(Set<Integer> segmentsToFilter, Set<?> keysToFilter) {
Stream<CacheEntry<K, V>> stream;
if (keysToFilter != null) {
if (trace) {
log.tracef("Applying key filtering %s", keysToFilter);
}
// Make sure we aren't going remote to retrieve these
AdvancedCache<K, V> advancedCache = AbstractDelegatingCache.unwrapCache(cache).getAdvancedCache()
.withFlags(Flag.CACHE_MODE_LOCAL);
// Need to box the key to get the correct segment
TypeConverter<Object, Object, Object, Object> typeConverter =
advancedCache.getComponentRegistry().getComponent(TypeConverter.class);
// We do type converter before getting CacheEntry, otherwise wrapper classes would have to both box and
// unbox, this way we avoid multiple calls and just do the one.
stream = keysToFilter.stream()
.map(typeConverter::boxKey)
.map(advancedCache::getCacheEntry)
.filter(e -> e != null);
} else {
stream = supplier.get();
}
if (segmentsToFilter != null && hash != null) {
if (trace) {
log.tracef("Applying segment filter %s", segmentsToFilter);
}
BitSet bitSet = new BitSet(hash.getNumSegments());
segmentsToFilter.forEach(bitSet::set);
stream = stream.filter(k -> bitSet.get(hash.getSegment(k.getKey())));
}
return stream;
}
@Override
public CloseableIterator<CacheEntry<K, V>> removableIterator(CloseableIterator<CacheEntry<K, V>> realIterator) {
return new RemovableCloseableIterator<>(realIterator, cache, CacheEntry::getKey);
}
}