package org.infinispan.query.impl.massindex; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.stream.Stream; import org.infinispan.Cache; import org.infinispan.commons.marshall.AbstractExternalizer; import org.infinispan.compat.TypeConverter; import org.infinispan.container.entries.CacheEntry; import org.infinispan.context.Flag; import org.infinispan.distexec.DistributedCallable; import org.infinispan.factories.ComponentRegistry; import org.infinispan.filter.AcceptAllKeyValueFilter; import org.infinispan.filter.CacheFilters; import org.infinispan.filter.KeyValueFilter; import org.infinispan.interceptors.locking.ClusteringDependentLogic; import org.infinispan.metadata.Metadata; import org.infinispan.query.impl.externalizers.ExternalizerIds; /** * Base class for mass indexer tasks. * @author gustavonalle * @since 7.1 */ public class IndexWorker implements DistributedCallable<Object, Object, Void> { protected final Class<?> entity; private final boolean flush; private final boolean clean; private final boolean primaryOwner; protected Cache<Object, Object> cache; protected TypeConverter typeConverter; protected IndexUpdater indexUpdater; private Set<Object> everywhereKeys; private Set<Object> keys = new HashSet<>(); private ClusteringDependentLogic clusteringDependentLogic; public IndexWorker(Class<?> entity, boolean flush, boolean clean, boolean primaryOwner, Set<Object> everywhereKeys) { this.entity = entity; this.flush = flush; this.clean = clean; this.primaryOwner = primaryOwner; this.everywhereKeys = everywhereKeys; } @Override public void setEnvironment(Cache<Object, Object> cache, Set<Object> inputKeys) { this.cache = cache; this.indexUpdater = new IndexUpdater(cache); ComponentRegistry componentRegistry = SecurityActions.getCacheComponentRegistry(cache.getAdvancedCache()); this.clusteringDependentLogic = componentRegistry.getComponent(ClusteringDependentLogic.class); this.typeConverter = componentRegistry.getComponent(TypeConverter.class); if (everywhereKeys != null && everywhereKeys.size() > 0) keys.addAll(everywhereKeys); if (inputKeys != null && inputKeys.size() > 0) keys.addAll(inputKeys); } protected void preIndex() { if (clean) indexUpdater.purge(entity); } protected void postIndex() { indexUpdater.waitForAsyncCompletion(); if (flush) indexUpdater.flush(entity); } private KeyValueFilter getFilter() { return primaryOwner ? new PrimaryOwnersKeyValueFilter() : AcceptAllKeyValueFilter.getInstance(); } private Object extractValue(Object wrappedValue) { if (typeConverter != null) { return typeConverter.unboxValue(wrappedValue); } return wrappedValue; } @Override @SuppressWarnings("unchecked") public Void call() throws Exception { Cache<Object, Object> unwrappedCache = SecurityActions.getUnwrappedCache(cache); if (keys == null || keys.size() == 0) { preIndex(); KeyValueFilter filter = getFilter(); try (Stream<CacheEntry<Object, Object>> stream = unwrappedCache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL) .cacheEntrySet().stream()) { Iterator<CacheEntry<Object, Object>> iterator = stream.filter(CacheFilters.predicate(filter)).iterator(); while (iterator.hasNext()) { CacheEntry<Object, Object> next = iterator.next(); Object value = extractValue(next.getValue()); if (value != null && value.getClass().equals(entity)) indexUpdater.updateIndex(next.getKey(), value); } } postIndex(); } else { Set<Class<?>> classSet = new HashSet<>(); for (Object key : keys) { Object value = extractValue(unwrappedCache.get(key)); if (value != null) { indexUpdater.updateIndex(key, value); classSet.add(value.getClass()); } } for (Class<?> clazz : classSet) indexUpdater.flush(clazz); } return null; } public static class Externalizer extends AbstractExternalizer<IndexWorker> { @Override public Set<Class<? extends IndexWorker>> getTypeClasses() { return Collections.singleton(IndexWorker.class); } @Override public void writeObject(ObjectOutput output, IndexWorker worker) throws IOException { output.writeObject(worker.entity); output.writeBoolean(worker.flush); output.writeBoolean(worker.clean); output.writeBoolean(worker.primaryOwner); output.writeObject(worker.everywhereKeys); } @Override public IndexWorker readObject(ObjectInput input) throws IOException, ClassNotFoundException { return new IndexWorker((Class<?>) input.readObject(), input.readBoolean(), input.readBoolean(), input.readBoolean(), (Set<Object>) input.readObject()); } @Override public Integer getId() { return ExternalizerIds.INDEX_WORKER; } } private class PrimaryOwnersKeyValueFilter implements KeyValueFilter { @Override public boolean accept(Object key, Object value, Metadata metadata) { return clusteringDependentLogic.getCacheTopology().getDistribution(key).isPrimary(); } } }