package org.infinispan.stream.impl; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.infinispan.Cache; import org.infinispan.distribution.ch.ConsistentHash; import org.infinispan.factories.annotations.Inject; import org.infinispan.factories.annotations.Start; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachelistener.annotation.PartitionStatusChanged; import org.infinispan.notifications.cachelistener.event.PartitionStatusChangedEvent; import org.infinispan.partitionhandling.AvailabilityException; import org.infinispan.partitionhandling.AvailabilityMode; /** * Cluster stream manager that also pays attention to partition status and properly closes iterators and throws * exceptions when the availability mode changes. */ public class PartitionAwareClusterStreamManager<K> extends ClusterStreamManagerImpl<K> { protected final PartitionListener listener; protected Cache<?, ?> cache; public PartitionAwareClusterStreamManager() { this.listener = new PartitionListener(); } @Listener protected class PartitionListener { protected volatile AvailabilityMode currentMode = AvailabilityMode.AVAILABLE; @PartitionStatusChanged public void onPartitionChange(PartitionStatusChangedEvent<K, ?> event) { if (!event.isPre()) { currentMode = event.getAvailabilityMode(); if (currentMode != AvailabilityMode.AVAILABLE) { // We just mark the iterator - relying on the fact that callers must call forget properly currentlyRunning.values().forEach(t -> markTrackerWithException(t, null, new AvailabilityException(), null)); } } } } @Inject public void inject(Cache<?, ?> cache) { this.cache = cache; } @Start public void start() { super.start(); cache.addListener(listener); } @Override public boolean awaitCompletion(Object id, long time, TimeUnit unit) throws InterruptedException { checkPartitionStatus(); return super.awaitCompletion(id, time, unit); } @Override public <R> Object remoteStreamOperation(boolean parallelDistribution, boolean parallelStream, ConsistentHash ch, Set<Integer> segments, Set<K> keysToInclude, Map<Integer, Set<K>> keysToExclude, boolean includeLoader, TerminalOperation<R> operation, ResultsCallback<R> callback, Predicate<? super R> earlyTerminatePredicate) { checkPartitionStatus(); return super.remoteStreamOperation(parallelDistribution, parallelStream, ch, segments, keysToInclude, keysToExclude, includeLoader, operation, callback, earlyTerminatePredicate); } @Override public <R> Object remoteStreamOperation(boolean parallelDistribution, boolean parallelStream, ConsistentHash ch, Set<Integer> segments, Set<K> keysToInclude, Map<Integer, Set<K>> keysToExclude, boolean includeLoader, KeyTrackingTerminalOperation<K, R, ?> operation, ResultsCallback<Collection<R>> callback) { checkPartitionStatus(); return super.remoteStreamOperation(parallelDistribution, parallelStream, ch, segments, keysToInclude, keysToExclude, includeLoader, operation, callback); } @Override public <R> Object remoteStreamOperationRehashAware(boolean parallelDistribution, boolean parallelStream, ConsistentHash ch, Set<Integer> segments, Set<K> keysToInclude, Map<Integer, Set<K>> keysToExclude, boolean includeLoader, TerminalOperation<R> operation, ResultsCallback<R> callback, Predicate<? super R> earlyTerminatePredicate) { checkPartitionStatus(); return super.remoteStreamOperationRehashAware(parallelDistribution, parallelStream, ch, segments, keysToInclude, keysToExclude, includeLoader, operation, callback, earlyTerminatePredicate); } @Override public <R2> Object remoteStreamOperationRehashAware(boolean parallelDistribution, boolean parallelStream, ConsistentHash ch, Set<Integer> segments, Set<K> keysToInclude, Map<Integer, Set<K>> keysToExclude, boolean includeLoader, KeyTrackingTerminalOperation<K, ?, R2> operation, ResultsCallback<Map<K, R2>> callback) { checkPartitionStatus(); return super.remoteStreamOperationRehashAware(parallelDistribution, parallelStream, ch, segments, keysToInclude, keysToExclude, includeLoader, operation, callback); } private void checkPartitionStatus() { if (listener.currentMode != AvailabilityMode.AVAILABLE) { throw log.partitionDegraded(); } } }