@Override public void put(K k, V v, Metadata metadata) { boolean l1Entry = false; if (metadata instanceof L1Metadata) { metadata = ((L1Metadata) metadata).metadata(); l1Entry = true; } 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, toStr(v)); } final InternalCacheEntry<K, V> copy; if (l1Entry) { copy = entryFactory.createL1(k, v, metadata); } else if (e != null) { copy = entryFactory.update(e, v, metadata); } else { // this is a brand-new entry copy = entryFactory.create(k, v, metadata); } if (trace) log.tracef("Store %s in container", copy); entries.compute( copy.getKey(), (key, entry) -> { activator.onUpdate(key, entry == null); return copy; }); }
/** * Loads the specified class using the passed classloader, or, if it is <code>null</code> the * Infinispan classes' classloader. * * <p>If loadtime instrumentation via GenerateInstrumentedClassLoader is used, this class may be * loaded by the bootstrap classloader. * * @param classname name of the class to load * @return the class * @param userClassLoader the application classloader which should be used to load the class, or * null if the class is always packaged with Infinispan * @throws ClassNotFoundException if the class cannot be loaded */ @SuppressWarnings("unchecked") public static <T> Class<T> loadClassStrict(String classname, ClassLoader userClassLoader) throws ClassNotFoundException { ClassLoader[] cls = getClassLoaders(userClassLoader); ClassNotFoundException e = null; NoClassDefFoundError ne = null; for (ClassLoader cl : cls) { if (cl == null) continue; try { return (Class<T>) Class.forName(classname, true, cl); } catch (ClassNotFoundException ce) { e = ce; } catch (NoClassDefFoundError ce) { ne = ce; } } if (e != null) throw e; else if (ne != null) { // Before we wrap this, make sure we appropriately log this. log.unableToLoadClass(classname, Arrays.toString(cls), ne); throw new ClassNotFoundException(classname, ne); } else throw new IllegalStateException(); }
@Override public InternalCacheEntry<K, V> remove(Object k) { final InternalCacheEntry<K, V>[] reference = new InternalCacheEntry[1]; entries.compute( (K) k, (key, entry) -> { activator.onRemove(key, entry == null); reference[0] = entry; return null; }); InternalCacheEntry<K, V> e = reference[0]; if (trace) { log.tracef("Removed %s from container", e); } return e == null || (e.canExpire() && e.isExpired(timeService.wallClockTime())) ? null : e; }
@Override public InternalCacheEntry<K, V> compute(K key, ComputeAction<K, V> action) { return entries.compute( key, (k, oldEntry) -> { InternalCacheEntry<K, V> newEntry = action.compute(k, oldEntry, entryFactory); if (newEntry == oldEntry) { return oldEntry; } else if (newEntry == null) { activator.onRemove(k, false); return null; } activator.onUpdate(k, oldEntry == null); if (trace) log.tracef("Store %s in container", newEntry); return newEntry; }); }
/** * 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 Cache<K, InternalCacheEntry<K, V>> evictionCache; protected InternalEntryFactory entryFactory; private EvictionManager evictionManager; private PassivationManager passivator; private ActivationManager activator; private PersistenceManager pm; private TimeService timeService; private CacheNotifier cacheNotifier; private ExpirationManager<K, V> expirationManager; public DefaultDataContainer(int concurrencyLevel) { // If no comparing implementations passed, could fallback on JDK CHM entries = CollectionFactory.makeConcurrentParallelMap(128, concurrencyLevel); evictionCache = null; } private static <K, V> Caffeine<K, V> caffeineBuilder() { return (Caffeine<K, V>) Caffeine.newBuilder(); } protected DefaultDataContainer( int concurrencyLevel, long thresholdSize, EvictionType thresholdPolicy) { DefaultEvictionListener evictionListener = new DefaultEvictionListener(); Caffeine<K, InternalCacheEntry<K, V>> caffeine = caffeineBuilder(); switch (thresholdPolicy) { case MEMORY: CacheEntrySizeCalculator<K, V> calc = new CacheEntrySizeCalculator<>( new WrappedByteArraySizeCalculator<>(new PrimitiveEntrySizeCalculator())); caffeine.weigher((k, v) -> (int) calc.calculateSize(k, v)).maximumWeight(thresholdSize); break; case COUNT: caffeine.maximumSize(thresholdSize); break; default: throw new UnsupportedOperationException("Policy not supported: " + thresholdPolicy); } evictionCache = applyListener(caffeine, evictionListener).build(); entries = evictionCache.asMap(); } private Caffeine<K, InternalCacheEntry<K, V>> applyListener( Caffeine<K, InternalCacheEntry<K, V>> caffeine, DefaultEvictionListener listener) { return caffeine .executor(new WithinThreadExecutor()) .removalListener( (k, v, c) -> { switch (c) { case SIZE: listener.onEntryEviction(Collections.singletonMap(k, v)); break; case EXPLICIT: listener.onEntryRemoved(new ImmortalCacheEntry(k, v)); break; case REPLACED: listener.onEntryActivated(k); break; } }) .writer( new CacheWriter<K, InternalCacheEntry<K, V>>() { @Override public void write(K key, InternalCacheEntry<K, V> value) {} @Override public void delete(K key, InternalCacheEntry<K, V> value, RemovalCause cause) { if (cause == RemovalCause.SIZE) { listener.onEntryChosenForEviction(new ImmortalCacheEntry(key, value)); } } }); } /** * Method invoked when memory policy is used * * @param concurrencyLevel * @param thresholdSize * @param sizeCalculator */ protected DefaultDataContainer( int concurrencyLevel, long thresholdSize, EntrySizeCalculator<? super K, ? super V> sizeCalculator) { DefaultEvictionListener evictionListener = new DefaultEvictionListener(); EntrySizeCalculator<K, InternalCacheEntry<K, V>> calc = new CacheEntrySizeCalculator<>(sizeCalculator); evictionCache = applyListener( Caffeine.newBuilder() .weigher((K k, InternalCacheEntry<K, V> v) -> (int) calc.calculateSize(k, v)) .maximumWeight(thresholdSize), evictionListener) .build(); entries = evictionCache.asMap(); } @Inject public void initialize( EvictionManager evictionManager, PassivationManager passivator, InternalEntryFactory entryFactory, ActivationManager activator, PersistenceManager clm, TimeService timeService, CacheNotifier cacheNotifier, ExpirationManager<K, V> expirationManager) { this.evictionManager = evictionManager; this.passivator = passivator; this.entryFactory = entryFactory; this.activator = activator; this.pm = clm; this.timeService = timeService; this.cacheNotifier = cacheNotifier; this.expirationManager = expirationManager; } public static <K, V> DefaultDataContainer<K, V> boundedDataContainer( int concurrencyLevel, long maxEntries, EvictionType thresholdPolicy) { return new DefaultDataContainer<>(concurrencyLevel, maxEntries, thresholdPolicy); } public static <K, V> DefaultDataContainer<K, V> boundedDataContainer( int concurrencyLevel, long maxEntries, EntrySizeCalculator<? super K, ? super V> sizeCalculator) { return new DefaultDataContainer<>(concurrencyLevel, maxEntries, sizeCalculator); } public static <K, V> DefaultDataContainer<K, V> unBoundedDataContainer(int concurrencyLevel) { return new DefaultDataContainer<>(concurrencyLevel); } @Override public InternalCacheEntry<K, V> peek(Object key) { if (entries instanceof PeekableMap) { return ((PeekableMap<K, InternalCacheEntry<K, V>>) entries).peek(key); } return entries.get(key); } @Override public InternalCacheEntry<K, V> get(Object k) { InternalCacheEntry<K, V> e = entries.get(k); if (e != null && e.canExpire()) { long currentTimeMillis = timeService.wallClockTime(); if (e.isExpired(currentTimeMillis)) { expirationManager.handleInMemoryExpiration(e, currentTimeMillis); e = null; } else { e.touch(currentTimeMillis); } } return e; } @Override public void put(K k, V v, Metadata metadata) { boolean l1Entry = false; if (metadata instanceof L1Metadata) { metadata = ((L1Metadata) metadata).metadata(); l1Entry = true; } 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, toStr(v)); } final InternalCacheEntry<K, V> copy; if (l1Entry) { copy = entryFactory.createL1(k, v, metadata); } else if (e != null) { copy = entryFactory.update(e, v, metadata); } else { // this is a brand-new entry copy = entryFactory.create(k, v, metadata); } if (trace) log.tracef("Store %s in container", copy); entries.compute( copy.getKey(), (key, entry) -> { activator.onUpdate(key, entry == null); return copy; }); } @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) { final InternalCacheEntry<K, V>[] reference = new InternalCacheEntry[1]; entries.compute( (K) k, (key, entry) -> { activator.onRemove(key, entry == null); reference[0] = entry; return null; }); InternalCacheEntry<K, V> e = reference[0]; if (trace) { log.tracef("Removed %s from container", e); } return e == null || (e.canExpire() && e.isExpired(timeService.wallClockTime())) ? null : e; } private Policy.Eviction<K, InternalCacheEntry<K, V>> eviction() { if (evictionCache != null) { Optional<Policy.Eviction<K, InternalCacheEntry<K, V>>> eviction = evictionCache.policy().eviction(); if (eviction.isPresent()) { return eviction.get(); } } throw new UnsupportedOperationException(); } @Override public long capacity() { Policy.Eviction<K, InternalCacheEntry<K, V>> evict = eviction(); return evict.getMaximum(); } @Override public void resize(long newSize) { Policy.Eviction<K, InternalCacheEntry<K, V>> evict = eviction(); evict.setMaximum(newSize); } @Override public int size() { int size = 0; // We have to loop through to make sure to remove expired entries for (Iterator<InternalCacheEntry<K, V>> iter = iterator(); iter.hasNext(); ) { iter.next(); if (++size == Integer.MAX_VALUE) return Integer.MAX_VALUE; } return size; } @Override public int sizeIncludingExpired() { 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 evict(K key) { entries.computeIfPresent( key, (o, entry) -> { passivator.passivate(entry); return null; }); } @Override public InternalCacheEntry<K, V> compute(K key, ComputeAction<K, V> action) { return entries.compute( key, (k, oldEntry) -> { InternalCacheEntry<K, V> newEntry = action.compute(k, oldEntry, entryFactory); if (newEntry == oldEntry) { return oldEntry; } else if (newEntry == null) { activator.onRemove(k, false); return null; } activator.onUpdate(k, oldEntry == null); if (trace) log.tracef("Store %s in container", newEntry); return newEntry; }); } @Override public Iterator<InternalCacheEntry<K, V>> iterator() { return new EntryIterator(entries.values().iterator(), false); } @Override public Iterator<InternalCacheEntry<K, V>> iteratorIncludingExpired() { return new EntryIterator(entries.values().iterator(), true); } 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(Entry<K, InternalCacheEntry<K, V>> entry) { passivator.passivate(entry.getValue()); } @Override public void onEntryActivated(Object key) { activator.onUpdate(key, true); } @Override public void onEntryRemoved(Entry<K, InternalCacheEntry<K, V>> entry) {} } private class ImmutableEntryIterator extends EntryIterator { ImmutableEntryIterator(Iterator<InternalCacheEntry<K, V>> it) { super(it, false); } @Override public InternalCacheEntry<K, V> next() { return CoreImmutables.immutableInternalCacheEntry(super.next()); } } public class EntryIterator implements Iterator<InternalCacheEntry<K, V>> { private final Iterator<InternalCacheEntry<K, V>> it; private final boolean includeExpired; private InternalCacheEntry<K, V> next; EntryIterator(Iterator<InternalCacheEntry<K, V>> it, boolean includeExpired) { this.it = it; this.includeExpired = includeExpired; } private InternalCacheEntry<K, V> getNext() { boolean initializedTime = false; long now = 0; while (it.hasNext()) { InternalCacheEntry<K, V> entry = it.next(); if (includeExpired || !entry.canExpire()) { return entry; } else { if (!initializedTime) { now = timeService.wallClockTime(); initializedTime = true; } if (!entry.isExpired(now)) { return entry; } } } return null; } @Override public InternalCacheEntry<K, V> next() { if (next == null) { next = getNext(); } if (next == null) { throw new NoSuchElementException(); } InternalCacheEntry<K, V> toReturn = next; next = null; return toReturn; } @Override public boolean hasNext() { if (next == null) { next = getNext(); } return next != null; } @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 BiConsumer<? 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"); entries.forEach( (K key, InternalCacheEntry<K, V> value) -> { if (filter.accept(key)) { action.accept(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 BiConsumer<? 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"); entries.forEach( (K key, InternalCacheEntry<K, V> value) -> { if (filter.accept(key, value.getValue(), value.getMetadata())) { action.accept(key, value); } }); // TODO figure out the way how to do interruption better (during iteration) if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } } }
@Override public void clear() { log.tracef("Clearing data container"); entries.clear(); }