Package org.infinispan.container

Source Code of org.infinispan.container.DefaultDataContainer$EntryIterator

package org.infinispan.container;

import net.jcip.annotations.ThreadSafe;

import org.infinispan.commons.equivalence.AnyEquivalence;
import org.infinispan.commons.equivalence.Equivalence;
import org.infinispan.commons.logging.Log;
import org.infinispan.commons.logging.LogFactory;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.commons.util.concurrent.ParallelIterableMap;
import org.infinispan.commons.util.concurrent.ParallelIterableMap.KeyValueAction;
import org.infinispan.commons.util.concurrent.jdk8backported.EquivalentConcurrentHashMapV8;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.eviction.ActivationManager;
import org.infinispan.eviction.EvictionManager;
import org.infinispan.eviction.EvictionStrategy;
import org.infinispan.eviction.EvictionThreadPolicy;
import org.infinispan.eviction.PassivationManager;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.filter.KeyFilter;
import org.infinispan.metadata.Metadata;
import org.infinispan.filter.KeyValueFilter;
import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.util.CoreImmutables;
import org.infinispan.util.TimeService;
import org.infinispan.util.concurrent.BoundedConcurrentHashMap;
import org.infinispan.util.concurrent.BoundedConcurrentHashMap.Eviction;
import org.infinispan.util.concurrent.BoundedConcurrentHashMap.EvictionListener;

import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import static org.infinispan.persistence.manager.PersistenceManager.AccessMode.BOTH;

/**
* DefaultDataContainer is both eviction and non-eviction based data container.
*
*
* @author Manik Surtani
* @author Galder Zamarreño
* @author Vladimir Blagojevic
* @author <a href="http://gleamynode.net/">Trustin Lee</a>
*
* @since 4.0
*/
@ThreadSafe
public class DefaultDataContainer<K, V> implements DataContainer<K, V> {

   private static final Log log = LogFactory.getLog(DefaultDataContainer.class);
   private static final boolean trace = log.isTraceEnabled();

   private final ConcurrentMap<K, InternalCacheEntry<K, V>> entries;
   private final DefaultEvictionListener evictionListener;
   private final ExtendedMap<K, V> extendedMap;
   protected InternalEntryFactory entryFactory;
   private EvictionManager evictionManager;
   private PassivationManager passivator;
   private ActivationManager activator;
   private PersistenceManager pm;
   private TimeService timeService;

   public DefaultDataContainer(int concurrencyLevel) {
      // If no comparing implementations passed, could fallback on JDK CHM
      entries = CollectionFactory.makeConcurrentParallelMap(128, concurrencyLevel);
      evictionListener = null;
      extendedMap = new EquivalentConcurrentExtendedMap();
   }

   public DefaultDataContainer(int concurrencyLevel,
         Equivalence<? super K> keyEq) {
      // If at least one comparing implementation give, use ComparingCHMv8
      entries = CollectionFactory.makeConcurrentParallelMap(128, concurrencyLevel, keyEq, AnyEquivalence.getInstance());
      evictionListener = null;
      extendedMap = new EquivalentConcurrentExtendedMap();
   }

   protected DefaultDataContainer(int concurrencyLevel, int maxEntries,
         EvictionStrategy strategy, EvictionThreadPolicy policy,
         Equivalence<? super K> keyEquivalence) {
      // translate eviction policy and strategy
      switch (policy) {
         case PIGGYBACK:
         case DEFAULT:
            evictionListener = new DefaultEvictionListener();
            break;
         default:
            throw new IllegalArgumentException("No such eviction thread policy " + strategy);
      }

      Eviction eviction;
      switch (strategy) {
         case FIFO:
         case UNORDERED:
         case LRU:
            eviction = Eviction.LRU;
            break;
         case LIRS:
            eviction = Eviction.LIRS;
            break;
         default:
            throw new IllegalArgumentException("No such eviction strategy " + strategy);
      }

      entries = new BoundedConcurrentHashMap<K, InternalCacheEntry<K, V>>(maxEntries, concurrencyLevel, eviction, evictionListener,
                                                                          keyEquivalence, AnyEquivalence.getInstance());
      extendedMap = new BoundedConcurrentExtendedMap();
   }

   @Inject
   public void initialize(EvictionManager evictionManager, PassivationManager passivator,
                          InternalEntryFactory entryFactory, ActivationManager activator, PersistenceManager clm, TimeService timeService) {
      this.evictionManager = evictionManager;
      this.passivator = passivator;
      this.entryFactory = entryFactory;
      this.activator = activator;
      this.pm = clm;
      this.timeService = timeService;
   }

   public static <K, V> DataContainer<K, V> boundedDataContainer(int concurrencyLevel, int maxEntries,
            EvictionStrategy strategy, EvictionThreadPolicy policy,
            Equivalence<? super K> keyEquivalence) {
      return new DefaultDataContainer(concurrencyLevel, maxEntries, strategy,
            policy, keyEquivalence);
   }

   public static <K, V> DataContainer<K, V> unBoundedDataContainer(int concurrencyLevel,
         Equivalence<? super K> keyEquivalence) {
      return new DefaultDataContainer(concurrencyLevel, keyEquivalence);
   }

   public static DataContainer unBoundedDataContainer(int concurrencyLevel) {
      return new DefaultDataContainer(concurrencyLevel);
   }

   @Override
   public InternalCacheEntry<K, V> peek(Object key) {
      return entries.get(key);
   }

   @Override
   public InternalCacheEntry<K, V> get(Object k) {
      InternalCacheEntry<K, V> e = peek(k);
      if (e != null && e.canExpire()) {
         long currentTimeMillis = timeService.wallClockTime();
         if (e.isExpired(currentTimeMillis)) {
            entries.remove(k);
            e = null;
         } else {
            e.touch(currentTimeMillis);
         }
      }
      return e;
   }

   @Override
   public void put(K k, V v, Metadata metadata) {
      InternalCacheEntry<K, V> e = entries.get(k);

      if (trace) {
         log.tracef("Creating new ICE for writing. Existing=%s, metadata=%s, new value=%s", e, metadata, v);
      }
      if (e != null) {
         e = entryFactory.update(e, v, metadata);
      } else {
         // this is a brand-new entry
         e = entryFactory.create(k, v, metadata);
      }

      if (trace)
         log.tracef("Store %s in container", e);

      extendedMap.putAndActivate(e);
   }

   @Override
   public boolean containsKey(Object k) {
      InternalCacheEntry<K, V> ice = peek(k);
      if (ice != null && ice.canExpire() && ice.isExpired(timeService.wallClockTime())) {
         entries.remove(k);
         ice = null;
      }
      return ice != null;
   }

   @Override
   public InternalCacheEntry<K, V> remove(Object k) {
      InternalCacheEntry<K, V> e = extendedMap.removeAndActivate(k);
      return e == null || (e.canExpire() && e.isExpired(timeService.wallClockTime())) ? null : e;
   }

   @Override
   public int size() {
      return entries.size();
   }

   @Override
   public void clear() {
      log.tracef("Clearing data container");
      entries.clear();
   }

   @Override
   public Set<K> keySet() {
      return Collections.unmodifiableSet(entries.keySet());
   }

   @Override
   public Collection<V> values() {
      return new Values();
   }

   @Override
   public Set<InternalCacheEntry<K, V>> entrySet() {
      return new EntrySet();
   }

   @Override
   public void purgeExpired() {
      long currentTimeMillis = timeService.wallClockTime();
      for (Iterator<InternalCacheEntry<K, V>> purgeCandidates = entries.values().iterator(); purgeCandidates.hasNext();) {
         InternalCacheEntry e = purgeCandidates.next();
         if (e.isExpired(currentTimeMillis)) {
            purgeCandidates.remove();
         }
      }
   }

   @Override
   public void evict(K key) {
      extendedMap.evict(key);
   }

   @Override
   public InternalCacheEntry<K, V> compute(K key, ComputeAction<K, V> action) {
      return extendedMap.compute(key, action);
   }

   @Override
   public Iterator<InternalCacheEntry<K, V>> iterator() {
      return new EntryIterator(entries.values().iterator());
   }

   private final class DefaultEvictionListener implements EvictionListener<K, InternalCacheEntry<K, V>> {

      @Override
      public void onEntryEviction(Map<K, InternalCacheEntry<K, V>> evicted) {
         evictionManager.onEntryEviction(evicted);
      }

      @Override
      public void onEntryChosenForEviction(InternalCacheEntry entry) {
         passivator.passivate(entry);
      }

      @Override
      public void onEntryActivated(Object key) {
         activator.onUpdate(key, true);
      }

      @Override
      public void onEntryRemoved(Object key) {
         if (pm != null)
            pm.deleteFromAllStores(key, BOTH);
      }
   }

   private static class ImmutableEntryIterator<K, V> extends EntryIterator<K, V> {
      ImmutableEntryIterator(Iterator<InternalCacheEntry<K, V>> it){
         super(it);
      }

      @Override
      public InternalCacheEntry<K, V> next() {
         return CoreImmutables.immutableInternalCacheEntry(super.next());
      }
   }

   public static class EntryIterator<K, V> implements Iterator<InternalCacheEntry<K, V>> {

      private final Iterator<InternalCacheEntry<K, V>> it;

      EntryIterator(Iterator<InternalCacheEntry<K, V>> it){this.it=it;}

      @Override
      public InternalCacheEntry<K, V> next() {
         return it.next();
      }

      @Override
      public boolean hasNext() {
         return it.hasNext();
      }

      @Override
      public void remove() {
         throw new UnsupportedOperationException();
      }
   }

   /**
    * Minimal implementation needed for unmodifiable Set
    *
    */
   private class EntrySet extends AbstractSet<InternalCacheEntry<K, V>> {

      @Override
      public boolean contains(Object o) {
         if (!(o instanceof Map.Entry)) {
            return false;
         }

         @SuppressWarnings("rawtypes")
         Map.Entry e = (Map.Entry) o;
         InternalCacheEntry ice = entries.get(e.getKey());
         if (ice == null) {
            return false;
         }
         return ice.getValue().equals(e.getValue());
      }

      @Override
      public Iterator<InternalCacheEntry<K, V>> iterator() {
         return new ImmutableEntryIterator(entries.values().iterator());
      }

      @Override
      public int size() {
         return entries.size();
      }

      @Override
      public String toString() {
         return entries.toString();
      }
   }

   /**
    * Minimal implementation needed for unmodifiable Collection
    *
    */
   private class Values extends AbstractCollection<V> {
      @Override
      public Iterator<V> iterator() {
         return new ValueIterator(entries.values().iterator());
      }

      @Override
      public int size() {
         return entries.size();
      }
   }

   private static class ValueIterator<K, V> implements Iterator<V> {
      Iterator<InternalCacheEntry<K, V>> currentIterator;

      private ValueIterator(Iterator<InternalCacheEntry<K, V>> it) {
         currentIterator = it;
      }

      @Override
      public boolean hasNext() {
         return currentIterator.hasNext();
      }

      @Override
      public void remove() {
         throw new UnsupportedOperationException();
      }

      @Override
      public V next() {
         return currentIterator.next().getValue();
      }
   }

   @Override
   public void executeTask(final KeyFilter<? super K> filter, final KeyValueAction<? super K, InternalCacheEntry<K, V>> action)
         throws InterruptedException {
      if (filter == null)
         throw new IllegalArgumentException("No filter specified");
      if (action == null)
         throw new IllegalArgumentException("No action specified");

      ParallelIterableMap<K, InternalCacheEntry<K, V>> map = (ParallelIterableMap<K, InternalCacheEntry<K, V>>) entries;
      map.forEach(32, new KeyValueAction<K, InternalCacheEntry<K, V>>() {
         @Override
         public void apply(K key, InternalCacheEntry<K, V> value) {
            if (filter.accept(key)) {
               action.apply(key, value);
            }
         }
      });
      //TODO figure out the way how to do interruption better (during iteration)
      if(Thread.currentThread().isInterrupted()){
         throw new InterruptedException();
      }
   }

   @Override
   public void executeTask(final KeyValueFilter<? super K, ? super V> filter, final KeyValueAction<? super K, InternalCacheEntry<K, V>> action)
         throws InterruptedException {
      if (filter == null)
         throw new IllegalArgumentException("No filter specified");
      if (action == null)
         throw new IllegalArgumentException("No action specified");

      ParallelIterableMap<K, InternalCacheEntry<K, V>> map = (ParallelIterableMap<K, InternalCacheEntry<K, V>>) entries;
      map.forEach(32, new KeyValueAction<K, InternalCacheEntry<K, V>>() {
         @Override
         public void apply(K key, InternalCacheEntry<K, V> value) {
            if (filter.accept(key, value.getValue(), value.getMetadata())) {
               action.apply(key, value);
            }
         }
      });
      //TODO figure out the way how to do interruption better (during iteration)
      if(Thread.currentThread().isInterrupted()){
         throw new InterruptedException();
      }
   }

   /**
    * Atomic logic to activate/passivate entries. This is dependent of the {@code ConcurrentMap} implementation.
    */
   private static interface ExtendedMap<K, V> {
      void evict(K key);

      InternalCacheEntry<K, V> compute(K key, ComputeAction<K, V> action);

      void putAndActivate(InternalCacheEntry<K, V> newEntry);

      InternalCacheEntry<K, V> removeAndActivate(Object key);
   }

   private class EquivalentConcurrentExtendedMap implements ExtendedMap<K, V> {
      @Override
      public void evict(K key) {
         ((EquivalentConcurrentHashMapV8<K, InternalCacheEntry<K, V>>) entries)
               .computeIfPresent(key, new EquivalentConcurrentHashMapV8.BiFun<K, InternalCacheEntry<K, V>, InternalCacheEntry<K, V>>() {
                  @Override
                  public InternalCacheEntry<K, V> apply(K o, InternalCacheEntry<K, V> entry) {
                     passivator.passivate(entry);
                     return null;
                  }
               });
      }

      @Override
      public InternalCacheEntry<K, V> compute(K key, final ComputeAction<K, V> action) {
         return ((EquivalentConcurrentHashMapV8<K, InternalCacheEntry<K, V>>) entries)
               .compute(key, new EquivalentConcurrentHashMapV8.BiFun<K, InternalCacheEntry<K, V>, InternalCacheEntry<K, V>>() {
                  @Override
                  public InternalCacheEntry<K, V> apply(K key, InternalCacheEntry<K, V> oldEntry) {
                     InternalCacheEntry<K, V> newEntry = action.compute(key, oldEntry, entryFactory);
                     if (newEntry == oldEntry) {
                        return oldEntry;
                     } else if (newEntry == null) {
                        activator.onRemove(key, false);
                        return null;
                     }
                     activator.onUpdate(key, oldEntry == null);
                     if (trace)
                        log.tracef("Store %s in container", newEntry);
                     return newEntry;
                  }
               });
      }

      @Override
      public void putAndActivate(final InternalCacheEntry<K, V> newEntry) {
         ((EquivalentConcurrentHashMapV8<K, InternalCacheEntry<K, V>>) entries)
               .compute(newEntry.getKey(), new EquivalentConcurrentHashMapV8.BiFun<K, InternalCacheEntry<K, V>, InternalCacheEntry<K, V>>() {
                  @Override
                  public InternalCacheEntry<K, V> apply(K key, InternalCacheEntry<K, V> entry) {
                     activator.onUpdate(key, entry == null);
                     return newEntry;
                  }
               });
      }

      @Override
      public InternalCacheEntry<K, V> removeAndActivate(Object key) {
         final AtomicReference<InternalCacheEntry<K,V>> reference = new AtomicReference<>(null);
         ((EquivalentConcurrentHashMapV8<Object, InternalCacheEntry<K, V>>) entries)
               .compute(key, new EquivalentConcurrentHashMapV8.BiFun<Object, InternalCacheEntry<K, V>, InternalCacheEntry<K, V>>() {
                  @Override
                  public InternalCacheEntry<K, V> apply(Object key, InternalCacheEntry<K, V> entry) {
                     activator.onRemove(key, entry == null);
                     reference.set(entry);
                     return null;
                  }
               });
         return reference.get();
      }
   }

   private class BoundedConcurrentExtendedMap implements ExtendedMap<K, V> {
      @Override
      public void evict(K key) {
         ((BoundedConcurrentHashMap<Object, InternalCacheEntry<K, V>>) entries).evict(key);
      }

      @Override
      public InternalCacheEntry<K, V> compute(K key, final ComputeAction<K, V> action) {
         final BoundedConcurrentHashMap<K, InternalCacheEntry<K, V>> boundedMap =
               ((BoundedConcurrentHashMap<K, InternalCacheEntry<K, V>>) entries);
         boundedMap.lock(key);
         try {
            InternalCacheEntry<K, V> oldEntry = boundedMap.get(key);
            InternalCacheEntry<K, V> newEntry = action.compute(key, oldEntry, entryFactory);
            if (oldEntry == newEntry) {
               return newEntry;
            } else if (newEntry == null) {
               activator.onRemove(key, false);
               boundedMap.remove(key);
               return null;
            }
            if (trace)
               log.tracef("Store %s in container", newEntry);
            //put already activate the entry if it is new.
            boundedMap.put(key, newEntry);
            return newEntry;
         } finally {
            boundedMap.unlock(key);
         }
      }

      @Override
      public void putAndActivate(InternalCacheEntry<K, V> newEntry) {
         //put already activate the entry if it is new.
         entries.put(newEntry.getKey(), newEntry);
      }

      @Override
      public InternalCacheEntry<K, V> removeAndActivate(Object key) {
         final BoundedConcurrentHashMap<Object, InternalCacheEntry<K, V>> boundedMap =
               ((BoundedConcurrentHashMap<Object, InternalCacheEntry<K, V>>) entries);
         boundedMap.lock(key);
         try {
            InternalCacheEntry<K, V> oldEntry = boundedMap.remove(key);
            activator.onRemove(key, oldEntry == null);
            return oldEntry;
         } finally {
            boundedMap.unlock(key);
         }
      }
   }
}
TOP

Related Classes of org.infinispan.container.DefaultDataContainer$EntryIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.